From 8126b2e44adf22479ef422249ff37821ebf60145 Mon Sep 17 00:00:00 2001 From: jeffgarratt Date: Mon, 9 Jan 2017 17:45:02 -0500 Subject: [PATCH] [FAB-1141] Bootstrap now creates local MSP config The boostrap feature now configures the local MSP folders (cacerts, signcerts, and keystore) properly. Added GenesisMethod parameter to orderer base docker compose file. Removed pre-V1 security configuration from docker compose file. Changed discovery configuration in docker compose to gossip mechanism. Change-Id: Ib97fca264eb5c93b81464ab3d76c55ccedd085ac Signed-off-by: jeffgarratt --- bddtests/compose-defaults.yml | 6 +++ bddtests/docker-compose-next-4.yml | 32 ++++++------ bddtests/docker-compose-orderer-base.yml | 1 + bddtests/features/bootstrap.feature | 20 ++++---- bddtests/steps/bootstrap_impl.py | 8 +++ bddtests/steps/bootstrap_util.py | 63 ++++++++++++++++++++++++ bddtests/steps/compose.py | 12 ++++- 7 files changed, 113 insertions(+), 29 deletions(-) diff --git a/bddtests/compose-defaults.yml b/bddtests/compose-defaults.yml index 324f46527bf..a607584b164 100644 --- a/bddtests/compose-defaults.yml +++ b/bddtests/compose-defaults.yml @@ -10,6 +10,8 @@ services: # TODO: This is currently required due to BUG in variant logic based upon log level. - CORE_LOGGING_LEVEL=DEBUG - CORE_PEER_NETWORKID=${CORE_PEER_NETWORKID} + volumes: + - ./volumes/peer:/var/hyperledger/bddtests/volumes/peer # Script will wait until membersrvc is up (if it exists) before starting # $$GOPATH (double dollar) required to prevent docker-compose doing its own # substitution before the value gets to the container @@ -22,3 +24,7 @@ services: membersrvc: image: hyperledger/fabric-membersrvc command: membersrvc + +volumes: + + peer: \ No newline at end of file diff --git a/bddtests/docker-compose-next-4.yml b/bddtests/docker-compose-next-4.yml index 5ca5886a82f..75260795ea9 100644 --- a/bddtests/docker-compose-next-4.yml +++ b/bddtests/docker-compose-next-4.yml @@ -13,7 +13,7 @@ services: service: orderer0 - vp0: + peer0: extends: file: docker-compose-next.yml service: vpNext @@ -21,8 +21,7 @@ services: - CORE_PEER_ID=vp0 - CORE_PEER_PROFILE_ENABLED=true - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 - - CORE_SECURITY_ENROLLID=test_vp0 - - CORE_SECURITY_ENROLLSECRET=MwYpmSRjupbT + - CORE_PEER_MSPCONFIGPATH=${PEER0_CORE_PEER_MSPCFGPATH} depends_on: - membersrvc0 - orderer0 @@ -30,47 +29,44 @@ services: # - 7050:6060 - vp1: + peer1: extends: file: docker-compose-next.yml service: vpNext environment: - CORE_PEER_ID=vp1 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0:7051 - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 - - CORE_SECURITY_ENROLLID=test_vp1 - - CORE_SECURITY_ENROLLSECRET=5wgHK9qqYaPy + - CORE_PEER_MSPCONFIGPATH=${PEER1_CORE_PEER_MSPCFGPATH} depends_on: - membersrvc0 - orderer0 - - vp0 + - peer0 - vp2: + peer2: extends: file: docker-compose-next.yml service: vpNext environment: - CORE_PEER_ID=vp2 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0:7051 - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 - - CORE_SECURITY_ENROLLID=test_vp2 - - CORE_SECURITY_ENROLLSECRET=vQelbRvja7cJ + - CORE_PEER_MSPCONFIGPATH=${PEER2_CORE_PEER_MSPCFGPATH} depends_on: - membersrvc0 - orderer0 - - vp0 + - peer0 - vp3: + peer3: extends: file: docker-compose-next.yml service: vpNext environment: - CORE_PEER_ID=vp3 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 + - CORE_PEER_GOSSIP_BOOTSTRAP=peer0:7051 - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 - - CORE_SECURITY_ENROLLID=test_vp3 - - CORE_SECURITY_ENROLLSECRET=9LKqKH5peurL + - CORE_PEER_MSPCONFIGPATH=${PEER3_CORE_PEER_MSPCFGPATH} depends_on: - membersrvc0 - orderer0 - - vp0 + - peer0 diff --git a/bddtests/docker-compose-orderer-base.yml b/bddtests/docker-compose-orderer-base.yml index ff9d6f87378..5b73a5d756b 100644 --- a/bddtests/docker-compose-orderer-base.yml +++ b/bddtests/docker-compose-orderer-base.yml @@ -11,6 +11,7 @@ services: - ORDERER_GENERAL_MAXWINDOWSIZE=1000 - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 - ORDERER_GENERAL_LOGLEVEL=debug + - ORDERER_GENERAL_GENESISMETHOD=provisional - ORDERER_GENERAL_GENESISIFILE=${ORDERER_GENERAL_GENESISIFILE} - ORDERER_RAMLEDGER_HISTORY_SIZE=100 volumes: diff --git a/bddtests/features/bootstrap.feature b/bddtests/features/bootstrap.feature index a5f13b051b0..cb69cf01355 100644 --- a/bddtests/features/bootstrap.feature +++ b/bddtests/features/bootstrap.feature @@ -25,7 +25,8 @@ Feature: Bootstrap # | User | Orderer | Organization | And the peer network has organizations: - | peerOrg0 | + | Organization | + | peerOrg0 | And a ordererBootstrapAdmin is identified and given access to all public certificates and orderer node info # Order info includes orderer admin/orderer information and address (host:port) from previous steps @@ -37,18 +38,17 @@ Feature: Bootstrap # to be used for setting the orderer genesis block path parameter in composition And the orderer admins use the genesis block for chain "" to configure orderers - And we compose "" - - # We now have an orderer network with NO peers. Now need to configure and start the peer network - And user requests role of peer admin by creating a key and csr for peer and acquires signed certificate from organization: - | peer0Admin | peer0 | peerOrg0 | - - # We now have an orderer network with NO peers. Now need to configure and start the peer network - And user requests role of peer signer by creating a key and csr for peer and acquires signed certificate from organization: - | peer0Signer | peer0 | peerOrg0 | + # This can be currently automated through folder creation of the proper form and placing PEMs. + And user requests role for peer by creating a key and csr for peer and acquires signed certificate from organization: + | User | Peer | Organization | + | peer0Signer | peer0 | peerOrg0 | + | peer1Signer | peer1 | peerOrg0 | + | peer2Signer | peer2 | peerOrg0 | + | peer3Signer | peer3 | peerOrg0 | + And we compose "" And peer admins get the genesis block for chain 'chain1' from chainBoostrapAdmin And the peer admins inspect and approve the genesis block for chain 'chain1' diff --git a/bddtests/steps/bootstrap_impl.py b/bddtests/steps/bootstrap_impl.py index e67ea3e58a2..57fc013ddf7 100644 --- a/bddtests/steps/bootstrap_impl.py +++ b/bddtests/steps/bootstrap_impl.py @@ -33,6 +33,13 @@ def step_impl(context): for row in context.table.rows: directory.registerOrdererAdminTuple(row['User'], row['Orderer'], row['Organization']) +@given(u'user requests role for peer by creating a key and csr for peer and acquires signed certificate from organization') +def step_impl(context): + assert 'table' in context, "Expected table with triplet of User/Peer/Organization" + directory = bootstrap_util.getDirectory(context) + for row in context.table.rows: + directory.registerOrdererAdminTuple(row['User'], row['Peer'], row['Organization']) + @given(u'the peer network has organizations') def step_impl(context): assert 'table' in context, "Expected table of peer network organizations" @@ -50,6 +57,7 @@ def step_impl(context): def step_impl(context, ordererSystemChainId, networkConfigPolicy, consensusType): genesisBlock = bootstrap_util.createGenesisBlock(context, ordererSystemChainId, networkConfigPolicy, consensusType) bootstrap_util.OrdererGensisBlockCompositionCallback(context, genesisBlock) + bootstrap_util.PeerCompositionCallback(context) @given(u'the orderer admins inspect and approve the genesis block for chain "{chainId}"') def step_impl(context, chainId): diff --git a/bddtests/steps/bootstrap_util.py b/bddtests/steps/bootstrap_util.py index faae4f4036a..ec3df279628 100644 --- a/bddtests/steps/bootstrap_util.py +++ b/bddtests/steps/bootstrap_util.py @@ -137,6 +137,9 @@ def __init__(self, name): # Which networks this organization belongs to self.networks = [] + def getSelfSignedCert(self): + return self.signedCert + def createCertificate(self, certReq): numYrs = 1 return createCertificate(certReq, (self.signedCert, self.pKey), 1000, (0, 60*60*24*365*numYrs)) @@ -413,6 +416,66 @@ def getEnv(self, composition, context, env): env["ORDERER_GENERAL_GENESISIFILE"]=self.getGenesisFilePath(composition, pathType=PathType.Container) +class PeerCompositionCallback(compose.CompositionCallback): + 'Responsible for setting up Peer nodes upon composition' + + def __init__(self, context): + self.context = context + self.volumeRootPathInContainer="/var/hyperledger/bddtests" + compose.Composition.RegisterCallbackInContext(context, self) + + def getVolumePath(self, composition, pathType=PathType.Local): + assert pathType in PathType, "Expected pathType of {0}".format(PathType) + basePath = "." + if pathType == PathType.Container: + basePath = self.volumeRootPathInContainer + return "{0}/volumes/peer/{1}".format(basePath, composition.projectName) + + def getPeerList(self, composition): + return [serviceName for serviceName in composition.getServiceNames() if "peer" in serviceName] + + def getLocalMspConfigPath(self, composition, peerService, pathType=PathType.Local): + return "{0}/{1}/localMspConfig".format(self.getVolumePath(composition, pathType), peerService) + + def _createLocalMspConfigDirs(self, mspConfigPath): + os.makedirs("{0}/{1}".format(mspConfigPath, "signcerts")) + os.makedirs("{0}/{1}".format(mspConfigPath, "admincerts")) + os.makedirs("{0}/{1}".format(mspConfigPath, "cacerts")) + os.makedirs("{0}/{1}".format(mspConfigPath, "keystore")) + + + def composing(self, composition, context): + 'Will copy local MSP info over at this point for each peer node' + + directory = getDirectory(context) + + for peerService in self.getPeerList(composition): + localMspConfigPath = self.getLocalMspConfigPath(composition, peerService) + self._createLocalMspConfigDirs(localMspConfigPath) + # Loop through directory and place Peer Organization Certs into cacerts folder + for peerOrg in [org for orgName,org in directory.organizations.items() if Network.Peer in org.networks]: + with open("{0}/cacerts/{1}.pem".format(localMspConfigPath, peerOrg.name), "w") as f: + f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, peerOrg.getSelfSignedCert())) + + # Find the peer signer Tuple for this peer and add to signcerts folder + for pnt, cert in [(peerNodeTuple,cert) for peerNodeTuple,cert in directory.ordererAdminTuples.items() if peerService in peerNodeTuple.user and "signer" in peerNodeTuple.user.lower()]: + # Put the PEM file in the signcerts folder + with open("{0}/signcerts/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: + f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) + # Put the associated private key into the keystore folder + user = directory.getUser(pnt.user, shouldCreate=False) + with open("{0}/keystore/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: + f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, user.pKey)) + + + def decomposing(self, composition, context): + 'Will remove the orderer volume path folder for the context' + shutil.rmtree(self.getVolumePath(composition)) + + def getEnv(self, composition, context, env): + for peerService in self.getPeerList(composition): + localMspConfigPath = self.getLocalMspConfigPath(composition, peerService, pathType=PathType.Container) + env["{0}_CORE_PEER_MSPCFGPATH".format(peerService.upper())]=localMspConfigPath def setOrdererBootstrapGenesisBlock(genesisBlock): 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' diff --git a/bddtests/steps/compose.py b/bddtests/steps/compose.py index 784991d5bef..958b935e19a 100644 --- a/bddtests/steps/compose.py +++ b/bddtests/steps/compose.py @@ -64,9 +64,19 @@ def __init__(self, context, composeFilesYaml, projectName = GetUUID()): self.context = context self.containerDataList = [] self.composeFilesYaml = composeFilesYaml + self.serviceNames = [] + self.serviceNames = self._collectServiceNames() [callback.composing(self, context) for callback in Composition.GetCompositionCallbacksFromContext(context)] self.issueCommand(["up", "--force-recreate", "-d"]) + def _collectServiceNames(self): + 'First collect the services names.' + servicesList = [service for service in self.issueCommand(["config", "--services"]).splitlines() if "WARNING" not in service] + return servicesList + + def getServiceNames(self): + return list(self.serviceNames) + def parseComposeFilesArg(self, composeFileArgs): args = [arg for sublist in [["-f", file] for file in [file if not os.path.isdir(file) else os.path.join(file, 'docker-compose.yml') for file in composeFileArgs.split()]] for arg in sublist] return args @@ -92,7 +102,7 @@ def issueCommand(self, args): output, error, returncode = \ bdd_test_util.cli_call(["docker-compose"] + cmdArgs, expect_success=True, env=self.getEnv()) # Don't rebuild if ps command - if args[0] !="ps": + if args[0] !="ps" and args[0] !="config": self.rebuildContainerData() return output