Skip to content

Commit

Permalink
add peers property support to fabric network config (#1329)
Browse files Browse the repository at this point in the history
Add ability to list 1 or more fabric peers in an organisation that can
be used to discover the fabric network. The new Peer Gateway connector
will only ever use the first one (Issue exists to allow for more gateway
peers to be used as load balancers)

This means connection profiles are not needed anymore reducing the
network configuration to a single file (ideally you would embed the
tlsCACert information as well

This will work for
- bound fabric 1.4 using gateway-enabled
- bound fabric 2.2
- bound fabric 2.4

It will not work with bound fabric 1.4 without gateway-enabled and the
connector will report a failure saying that it cannot work with a
dynamic connection profile (there is an issue to enable dynamic profile
support for this but the 1.4 connector is legacy and not worth investing
in)

The implementation works by generating a dynamic connection profile from
the peer information as all the connectors work by working to an
interface which abstracts the required information but the
implementation gets that information by processing an in memory version
of the connection profile and doesn't make sense to have 2 different in
memory representations.

Documentation still to be provided

Signed-off-by: D <[email protected]>
  • Loading branch information
davidkel authored May 12, 2022
1 parent ca58c47 commit 1a6639c
Show file tree
Hide file tree
Showing 36 changed files with 623 additions and 267 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,30 +194,30 @@ class ConnectionProfileDefinition {

/**
* Return the list of peers for organization of the connection profile
* @param {*} mspid the mspId of the org
* @param {*} mspId the mspId of the org
* @returns {[string]} an array containing the list of all the peers
*/
getPeersListForOrganization(mspid) {
getPeersListForOrganization(mspId) {
if (!this.connectionProfile.organizations) {
throw new Error('No organizations property can be found for the connection profile provided');
}

for (const org in this.connectionProfile.organizations) {
if (this.connectionProfile.organizations[org].mspid === mspid) {
if (this.connectionProfile.organizations[org].mspid === mspId) {
const peers = this.connectionProfile.organizations[org].peers;

if (!peers) {
throw new Error(`Org with mspid ${mspid} listed in connectionProfile.organizations does not have any peers property`);
throw new Error(`Org with mspid ${mspId} listed in connectionProfile.organizations does not have any peers property`);
}
if (peers.length === 0) {
throw new Error(`Org with mspid ${mspid} has a peers property but it is empty`);
throw new Error(`Org with mspid ${mspId} has a peers property but it is empty`);
}

return peers;
}
}

throw new Error(`Org with mspid ${mspid} cannot be found in connectionProfile.organizations`);
throw new Error(`Org with mspid ${mspId} cannot be found in connectionProfile.organizations`);
}


Expand All @@ -226,11 +226,11 @@ class ConnectionProfileDefinition {
* @param {string} peer the name of the peer
* @returns {string} tls certificate
*/
async getTlsCertForPeer(peer) {
async getTlsCACertsForPeer(peer) {
const peerObj = this._getPeerIfValid(peer);

if (!peerObj.tlsCACerts) {
throw new Error(`No tlsCACert property of ${peer} in the connection profile was not provided`);
throw new Error(`No tlsCACerts property for ${peer} in the connection profile was provided`);
}

if (peerObj.tlsCACerts.pem) {
Expand Down Expand Up @@ -258,7 +258,7 @@ class ConnectionProfileDefinition {

return pem;
} else {
throw new Error(`No valid tls cert option provided in the ${peer}.tlsCACert property of connection profile`);
throw new Error(`No valid tls cert option provided in the ${peer}.tlsCACerts property of connection profile`);
}
}

Expand Down Expand Up @@ -305,6 +305,9 @@ class ConnectionProfileDefinition {
* @returns {*} the peer object
*/
_getPeerIfValid(peer) {
if (!peer) {
throw new Error('No peer provided to locate in connection profile definition');
}
if (!this.connectionProfile.peers) {
throw new Error('No peers property can be found in the connection profile provided');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,22 +160,90 @@ class ConnectorConfiguration {

if (filteredOrganizationList.length > 0) {
const connectionProfileEntry = filteredOrganizationList[0].connectionProfile;
if (!connectionProfileEntry) {
throw new Error(`No connection profile entry for organization ${mspId} has been defined`);
if (connectionProfileEntry) {
return await this._parseConnectionProfile(mspId, connectionProfileEntry);
}

if (!connectionProfileEntry.path) {
throw new Error(`No path for the connection profile for organization ${mspId} has been defined`);
const peersEntry = filteredOrganizationList[0].peers;
if (peersEntry) {
return this._parsePeersIntoConnectionProfileDefinition(mspId, peersEntry);
}

if (!connectionProfileEntry.loadedConnectionProfile) {
connectionProfileEntry.loadedConnectionProfile = await this._loadConnectionProfile(connectionProfileEntry.path);
throw new Error(`No connection profile or peers entry for organization ${mspId} has been defined`);
}

throw new Error(`No organization defined for ${mspId}`);
}

/**
* Parse the network config reference to a connection profile
* @param {*} mspId The msp ID of the organization
* @param {*} connectionProfileEntry the network config definition of a connection profile
* @returns {Promise<ConnectionProfileDefinition>} A connection profile definition
* @async
*/
async _parseConnectionProfile(mspId, connectionProfileEntry) {

if (!connectionProfileEntry.path) {
throw new Error(`No path for the connection profile for organization ${mspId} has been defined`);
}

if (!connectionProfileEntry.loadedConnectionProfile) {
connectionProfileEntry.loadedConnectionProfile = await this._loadConnectionProfile(connectionProfileEntry.path);
}

return new ConnectionProfileDefinition(mspId, connectionProfileEntry);
}

/**
* Parse the peers section in the network config into a connection profile defintion
* @param {*} mspId The msp ID of the organization
* @param {*} peersEntry the peers section in the network config
* @returns {ConnectionProfileDefinition} A connection profile definition
*/
_parsePeersIntoConnectionProfileDefinition(mspId, peersEntry) {
const connectionProfileEntry = {
discover: true,
loadedConnectionProfile: {
name: 'network',
version: '1.0.0',
client: {
organization: mspId
},
organizations: {
},
peers: {}
}
};

connectionProfileEntry.loadedConnectionProfile.organizations[mspId] = {
mspid: mspId,
peers: []
};

for (const peer of peersEntry) {
if (!peer.endpoint) {
throw new Error(`Peers are defined for organization ${mspId} but one or more entries do not have an endpoint defined`);
}

return new ConnectionProfileDefinition(mspId, connectionProfileEntry);
const peerURL = (peer.tlsCACerts ? 'grpcs://' : 'grpc://') + peer.endpoint;
const uniquePeerName = `${mspId}_${peer.endpoint}`;
connectionProfileEntry.loadedConnectionProfile.peers[uniquePeerName] = {
url: peerURL
};

if (peer.tlsCACerts) {
connectionProfileEntry.loadedConnectionProfile.peers[uniquePeerName].tlsCACerts = peer.tlsCACerts;
}

if (peer.grpcOptions) {
connectionProfileEntry.loadedConnectionProfile.peers[uniquePeerName].grpcOptions = peer.grpcOptions;
}

connectionProfileEntry.loadedConnectionProfile.organizations[mspId].peers.push(uniquePeerName);
}

throw new Error(`No organization defined for ${mspId}`);
return new ConnectionProfileDefinition(mspId, connectionProfileEntry);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ class PeerGateway extends ConnectorBase {
}

// create grpcs client with the tlsCredentials of the peer
const tlsRootCert = await connectionProfileDefinition.getTlsCertForPeer(peer);
const tlsRootCert = await connectionProfileDefinition.getTlsCACertsForPeer(peer);
const tlsCredentials = grpc.credentials.createSsl(Buffer.from(tlsRootCert));
client = new grpc.Client(peerEndpoint, tlsCredentials, grpcOptions);
} else {
Expand Down
16 changes: 0 additions & 16 deletions packages/caliper-fabric/lib/identity-management/IdentityManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,29 +190,13 @@ class IdentityManager {
if (organization.identities.wallet) {
await this._extractIdentitiesFromWallet(organization.mspid, organization.identities.wallet);
}

if (organization.identities.credentialStore) {
await this._extractIdentitiesFromCredentialStore(organization.mspid, organization.identities.credentialStore);
}

}
}
} else {
throw new Error('No organizations have been defined');
}
}

/**
* Extract identities from a fabric node sdk 1.4 credential store and store in the in memory wallet
* @param {string} mspId mspId of the organisation
* @param {*} credentialStore the credential store information
* @async
* @private
*/
async _extractIdentitiesFromCredentialStore(mspId, credentialStore) {
// TODO: To be implemented
}

/**
* Extract identities from a version specific wallet and store in the in memory wallet
* @param {string} mspId mspId of the organisation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ describe('A Connection Profile Definition', async () => {
connectionProfileDefinition.isDynamicConnectionProfile().should.equal(false);
});


it('should return true if a connection profile is using tls', () => {
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: JSON.parse(connectionProfile.toString()),
Expand Down Expand Up @@ -438,7 +437,6 @@ describe('A Connection Profile Definition', async () => {
});

describe('when getting the list of peer for the organization', () => {

it('should return the list of all peers name in the organization', () => {
const limitedConnectionProfile = {
organizations: {
Expand Down Expand Up @@ -574,13 +572,21 @@ describe('A Connection Profile Definition', async () => {

const peer = 'peer0.org1.example.com';

it('should throw an error if no peer is provided', async() => {
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: JSON.parse(connectionProfile.toString()),
discover: true
});
await connectionProfileDefinition.getTlsCACertsForPeer().should.be.rejectedWith(/No peer provided to locate in connection profile definition/);
});

it('should get the pem if embeded in the connectioProfile under .tlsCACerts.pem', async () => {
const dynamicConnectionProfile = JSON.parse(connectionProfile.toString());
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: dynamicConnectionProfile,
discover: true
});
(await connectionProfileDefinition.getTlsCertForPeer(peer)).should.deep.equal(
(await connectionProfileDefinition.getTlsCACertsForPeer(peer)).should.deep.equal(
dynamicConnectionProfile.peers[peer].tlsCACerts.pem
);
});
Expand All @@ -601,7 +607,7 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: invalidPathConnectionProfile,
discover: true
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
`path property does not point to a file that exists at ${path} for ${peer}`
);
});
Expand All @@ -621,7 +627,7 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: validPathConnectionProfile,
discover: false
});
(await connectionProfileDefinition.getTlsCertForPeer(peer)).should.deep.equal(
(await connectionProfileDefinition.getTlsCACertsForPeer(peer)).should.deep.equal(
'-----BEGIN CERTIFICATE-----\nMIICKzCCAdGgAwIBAgIRAL0i4WmltsbdL5xDc0xJQYQwCgYIKoZIzj0EAwIwczEL\nMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBG\ncmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMTE2Nh\nLm9yZzEuZXhhbXBsZS5jb20wHhcNMjAwOTA3MTE0MjAwWhcNMzAwOTA1MTE0MjAw\nWjBsMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMN\nU2FuIEZyYW5jaXNjbzEPMA0GA1UECxMGY2xpZW50MR8wHQYDVQQDDBZVc2VyMUBv\ncmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZ6BjhMNZ\nPjLYxx+Mtq08UY7Tmill5xRqbACy13wZCmb8SIW6/pjzhWVWfM7YoSLGQWgrgiB4\n8NU8eubMyQA3DqNNMEswDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwKwYD\nVR0jBCQwIoAgnvPwKjaMDSoQBDUfZMgJPmr5nlvrV/AdzLomWFMuLbkwCgYIKoZI\nzj0EAwIDSAAwRQIhAJwCKxXrCGZMgBlxbaMJzN7wcUM2qjX8jS4ZnBDl7HpaAiBH\nNhHITMTKPcPKgrQT/h1bTXqmxZXnwgh1n7D7VC/Fuw==\n-----END CERTIFICATE-----\n'
);
});
Expand All @@ -640,8 +646,8 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: noPemConnectionProfile,
discover: false
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
`No valid tls cert option provided in the ${peer}.tlsCACert property of connection profile`
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
`No valid tls cert option provided in the ${peer}.tlsCACerts property of connection profile`
);
});

Expand All @@ -657,8 +663,8 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: notlsCACertConnectionProfile,
discover: false
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
`No tlsCACert property of ${peer} in the connection profile was not provided`
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
`No tlsCACerts property for ${peer} in the connection profile was provided`
);
});

Expand All @@ -667,7 +673,7 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: noPeerConnectionProfile,
discover: false
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
`${peer} provided is not present in the connection profile`
);
});
Expand All @@ -677,7 +683,7 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: blankConnectionProfile,
discover: false
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
'No peers property can be found in the connection profile provided'
);
});
Expand All @@ -697,10 +703,11 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: invalidPemConnectionProfile,
discover: false
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
`pem provided for ${peer} in the connection profile .tlsCACerts.pem is not valid`
);
});

it('should throw an error if path provided through tlsCAcerts.path does not point to a valid cert', async () => {
const invalidPemConnectionProfile = {
peers: {
Expand All @@ -716,7 +723,7 @@ describe('A Connection Profile Definition', async () => {
loadedConnectionProfile: invalidPemConnectionProfile,
discover: false
});
await connectionProfileDefinition.getTlsCertForPeer(peer).should.be.rejectedWith(
await connectionProfileDefinition.getTlsCACertsForPeer(peer).should.be.rejectedWith(
`path property does not point to a valid pem file for ${path} for ${peer}`
);
});
Expand All @@ -727,6 +734,17 @@ describe('A Connection Profile Definition', async () => {

const peer = 'peer0.org1.example.com';

it('should throw an error if no peer is provided', async() => {
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: JSON.parse(connectionProfile.toString()),
discover: true
});

(() => {
connectionProfileDefinition.getGrpcEndPointForPeer();
}).should.throw(/No peer provided to locate in connection profile definition/);
});

it('should return the correct endpoint for a grpcs url', () => {
const grpcsConnectionProfile = {
peers: {
Expand Down Expand Up @@ -823,6 +841,16 @@ describe('A Connection Profile Definition', async () => {

const peer = 'peer0.org1.example.com';

it('should throw an error if no peer is provided', async() => {
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: JSON.parse(connectionProfile.toString()),
discover: true
});
(() => {
connectionProfileDefinition.getGrpcOptionsForPeer();
}).should.throw(/No peer provided to locate in connection profile definition/);
});

it('should return the defined grpcOptions if present', () => {
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: JSON.parse(connectionProfile.toString()),
Expand Down Expand Up @@ -881,6 +909,17 @@ describe('A Connection Profile Definition', async () => {

const peer = 'peer0.org1.example.com';

it('should throw an error if no peer is provided', async() => {
const connectionProfileDefinition = new ConnectionProfileDefinition(mspId, {
loadedConnectionProfile: JSON.parse(connectionProfile.toString()),
discover: true
});

(() => {
connectionProfileDefinition.isTLSRequiredForEndpoint();
}).should.throw(/No peer provided to locate in connection profile definition/);
});

it('should return true for a grpcs url ', () => {
const grpcsConnectionProfile = {
peers: {
Expand Down
Loading

0 comments on commit 1a6639c

Please sign in to comment.