diff --git a/bddtests/bddtests_test.go b/bddtests/bddtests_test.go new file mode 100644 index 00000000000..ff7829f0d09 --- /dev/null +++ b/bddtests/bddtests_test.go @@ -0,0 +1,30 @@ +package bddtests + +import "github.com/DATA-DOG/godog" + +func FeatureContext(s *godog.Suite) { + bddCtx := &BDDContext{godogSuite: s, users: make(map[string]*UserRegistration), grpcClientPort: 7051} + + s.BeforeScenario(bddCtx.beforeScenario) + s.AfterScenario(bddCtx.afterScenarioDecompose) + + FeatureContextBootstrap(bddCtx, s) + + s.Step(`^we compose "([^"]*)"$`, bddCtx.weCompose) + s.Step(`^requesting "([^"]*)" from "([^"]*)"$`, bddCtx.requestingFrom) + s.Step(`^I should get a JSON response with array "([^"]*)" contains "([^"]*)" elements$`, bddCtx.iShouldGetAJSONResponseWithArrayContainsElements) + s.Step(`^I wait "([^"]*)" seconds$`, bddCtx.iWaitSeconds) + s.Step(`^I register with CA supplying username "([^"]*)" and secret "([^"]*)" on peers:$`, bddCtx.iRegisterWithCASupplyingUsernameAndSecretOnPeers) + s.Step(`^user "([^"]*)" creates a chaincode spec "([^"]*)" of type "([^"]*)" for chaincode "([^"]*)" with args$`, bddCtx.userCreatesAChaincodeSpecOfTypeForChaincodeWithArgs) + + s.Step(`^user "([^"]*)" creates a deployment spec "([^"]*)" using chaincode spec "([^"]*)" and devops on peer "([^"]*)"$`, bddCtx.userCreatesADeploymentSpecUsingChaincodeSpecAndDevopsOnPeer) + //s.Step(`^user "([^"]*)" creates a deployment spec "([^"]*)" using chaincode spec "([^"]*)"$`, bddCtx.userCreatesADeploymentSpecUsingChaincodeSpec) + + s.Step(`^user "([^"]*)" creates a deployment proposal "([^"]*)" using chaincode deployment spec "([^"]*)"$`, bddCtx.userCreatesADeploymentProposalUsingChaincodeDeploymentSpec) + s.Step(`^user "([^"]*)" sends proposal "([^"]*)" to endorsers with timeout of "([^"]*)" seconds:$`, bddCtx.userSendsProposalToEndorsersWithTimeoutOfSeconds) + s.Step(`^user "([^"]*)" stores their last result as "([^"]*)"$`, bddCtx.userStoresTheirLastResultAs) + s.Step(`^user "([^"]*)" expects proposal responses "([^"]*)" with status "([^"]*)" from endorsers:$`, bddCtx.userExpectsProposalResponsesWithStatusFromEndorsers) + s.Step(`^user "([^"]*)" sets ESCC to "([^"]*)" for chaincode spec "([^"]*)"$`, bddCtx.userSetsESCCToForChaincodeSpec) + s.Step(`^user "([^"]*)" sets VSCC to "([^"]*)" for chaincode spec "([^"]*)"$`, bddCtx.userSetsVSCCToForChaincodeSpec) +} + diff --git a/bddtests/common/common_pb2.py b/bddtests/common/common_pb2.py index 3dd8c53a571..d1ba5c67ff6 100644 --- a/bddtests/common/common_pb2.py +++ b/bddtests/common/common_pb2.py @@ -21,7 +21,7 @@ name='common/common.proto', package='common', syntax='proto3', - serialized_pb=_b('\n\x13\x63ommon/common.proto\x12\x06\x63ommon\x1a\x1fgoogle/protobuf/timestamp.proto\"d\n\x06Header\x12(\n\x0b\x63hainHeader\x18\x01 \x01(\x0b\x32\x13.common.ChainHeader\x12\x30\n\x0fsignatureHeader\x18\x02 \x01(\x0b\x32\x17.common.SignatureHeader\"l\n\x0b\x43hainHeader\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12-\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x63hainID\x18\x04 \x01(\x0c\"@\n\x0fSignatureHeader\x12\x0f\n\x07\x63reator\x18\x01 \x01(\x0c\x12\r\n\x05nonce\x18\x02 \x01(\x0c\x12\r\n\x05\x65poch\x18\x03 \x01(\x04\"7\n\x07Payload\x12\x1e\n\x06header\x18\x01 \x01(\x0b\x32\x0e.common.Header\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\".\n\x08\x45nvelope\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"v\n\x05\x42lock\x12#\n\x06Header\x18\x01 \x01(\x0b\x32\x13.common.BlockHeader\x12\x1f\n\x04\x44\x61ta\x18\x02 \x01(\x0b\x32\x11.common.BlockData\x12\'\n\x08Metadata\x18\x03 \x01(\x0b\x32\x15.common.BlockMetadata\"E\n\x0b\x42lockHeader\x12\x0e\n\x06Number\x18\x01 \x01(\x04\x12\x14\n\x0cPreviousHash\x18\x02 \x01(\x0c\x12\x10\n\x08\x44\x61taHash\x18\x03 \x01(\x0c\"\x19\n\tBlockData\x12\x0c\n\x04\x44\x61ta\x18\x01 \x03(\x0c\"!\n\rBlockMetadata\x12\x10\n\x08Metadata\x18\x01 \x03(\x0c*\x8b\x01\n\x06Status\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x07SUCCESS\x10\xc8\x01\x12\x10\n\x0b\x42\x41\x44_REQUEST\x10\x90\x03\x12\x0e\n\tFORBIDDEN\x10\x93\x03\x12\x0e\n\tNOT_FOUND\x10\x94\x03\x12\x1a\n\x15INTERNAL_SERVER_ERROR\x10\xf4\x03\x12\x18\n\x13SERVICE_UNAVAILABLE\x10\xf7\x03*j\n\nHeaderType\x12\x0b\n\x07MESSAGE\x10\x00\x12\x1d\n\x19\x43ONFIGURATION_TRANSACTION\x10\x01\x12\x16\n\x12\x43ONFIGURATION_ITEM\x10\x02\x12\x18\n\x14\x45NDORSER_TRANSACTION\x10\x03\x42-Z+github.com/hyperledger/fabric/protos/commonb\x06proto3') + serialized_pb=_b('\n\x13\x63ommon/common.proto\x12\x06\x63ommon\x1a\x1fgoogle/protobuf/timestamp.proto\"d\n\x06Header\x12(\n\x0b\x63hainHeader\x18\x01 \x01(\x0b\x32\x13.common.ChainHeader\x12\x30\n\x0fsignatureHeader\x18\x02 \x01(\x0b\x32\x17.common.SignatureHeader\"\x9c\x01\n\x0b\x43hainHeader\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12-\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07\x63hainID\x18\x04 \x01(\x0c\x12\x0c\n\x04txID\x18\x05 \x01(\t\x12\r\n\x05\x65poch\x18\x06 \x01(\x04\x12\x11\n\textension\x18\x07 \x01(\x0c\"1\n\x0fSignatureHeader\x12\x0f\n\x07\x63reator\x18\x01 \x01(\x0c\x12\r\n\x05nonce\x18\x02 \x01(\x0c\"7\n\x07Payload\x12\x1e\n\x06header\x18\x01 \x01(\x0b\x32\x0e.common.Header\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\".\n\x08\x45nvelope\x12\x0f\n\x07payload\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"v\n\x05\x42lock\x12#\n\x06Header\x18\x01 \x01(\x0b\x32\x13.common.BlockHeader\x12\x1f\n\x04\x44\x61ta\x18\x02 \x01(\x0b\x32\x11.common.BlockData\x12\'\n\x08Metadata\x18\x03 \x01(\x0b\x32\x15.common.BlockMetadata\"E\n\x0b\x42lockHeader\x12\x0e\n\x06Number\x18\x01 \x01(\x04\x12\x14\n\x0cPreviousHash\x18\x02 \x01(\x0c\x12\x10\n\x08\x44\x61taHash\x18\x03 \x01(\x0c\"\x19\n\tBlockData\x12\x0c\n\x04\x44\x61ta\x18\x01 \x03(\x0c\"!\n\rBlockMetadata\x12\x10\n\x08Metadata\x18\x01 \x03(\x0c*\x8b\x01\n\x06Status\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x07SUCCESS\x10\xc8\x01\x12\x10\n\x0b\x42\x41\x44_REQUEST\x10\x90\x03\x12\x0e\n\tFORBIDDEN\x10\x93\x03\x12\x0e\n\tNOT_FOUND\x10\x94\x03\x12\x1a\n\x15INTERNAL_SERVER_ERROR\x10\xf4\x03\x12\x18\n\x13SERVICE_UNAVAILABLE\x10\xf7\x03*j\n\nHeaderType\x12\x0b\n\x07MESSAGE\x10\x00\x12\x1d\n\x19\x43ONFIGURATION_TRANSACTION\x10\x01\x12\x16\n\x12\x43ONFIGURATION_ITEM\x10\x02\x12\x18\n\x14\x45NDORSER_TRANSACTION\x10\x03\x42-Z+github.com/hyperledger/fabric/protos/commonb\x06proto3') , dependencies=[google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR,]) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -63,8 +63,8 @@ ], containing_type=None, options=None, - serialized_start=701, - serialized_end=840, + serialized_start=735, + serialized_end=874, ) _sym_db.RegisterEnumDescriptor(_STATUS) @@ -94,8 +94,8 @@ ], containing_type=None, options=None, - serialized_start=842, - serialized_end=948, + serialized_start=876, + serialized_end=982, ) _sym_db.RegisterEnumDescriptor(_HEADERTYPE) @@ -187,6 +187,27 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), + _descriptor.FieldDescriptor( + name='txID', full_name='common.ChainHeader.txID', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='epoch', full_name='common.ChainHeader.epoch', index=5, + number=6, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='extension', full_name='common.ChainHeader.extension', index=6, + number=7, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), ], extensions=[ ], @@ -199,8 +220,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=166, - serialized_end=274, + serialized_start=167, + serialized_end=323, ) @@ -225,13 +246,6 @@ message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), - _descriptor.FieldDescriptor( - name='epoch', full_name='common.SignatureHeader.epoch', index=2, - number=3, type=4, cpp_type=4, label=1, - has_default_value=False, default_value=0, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None), ], extensions=[ ], @@ -244,8 +258,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=276, - serialized_end=340, + serialized_start=325, + serialized_end=374, ) @@ -282,8 +296,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=342, - serialized_end=397, + serialized_start=376, + serialized_end=431, ) @@ -320,8 +334,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=399, - serialized_end=445, + serialized_start=433, + serialized_end=479, ) @@ -365,8 +379,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=447, - serialized_end=565, + serialized_start=481, + serialized_end=599, ) @@ -410,8 +424,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=567, - serialized_end=636, + serialized_start=601, + serialized_end=670, ) @@ -441,8 +455,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=638, - serialized_end=663, + serialized_start=672, + serialized_end=697, ) @@ -472,8 +486,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=665, - serialized_end=698, + serialized_start=699, + serialized_end=732, ) _HEADER.fields_by_name['chainHeader'].message_type = _CHAINHEADER diff --git a/bddtests/common/configuration_pb2.py b/bddtests/common/configuration_pb2.py index 3bda93f37d4..c906f23f157 100644 --- a/bddtests/common/configuration_pb2.py +++ b/bddtests/common/configuration_pb2.py @@ -20,7 +20,7 @@ name='common/configuration.proto', package='common', syntax='proto3', - serialized_pb=_b('\n\x1a\x63ommon/configuration.proto\x12\x06\x63ommon\x1a\x13\x63ommon/common.proto\"G\n\x15\x43onfigurationEnvelope\x12.\n\x05Items\x18\x01 \x03(\x0b\x32\x1f.common.SignedConfigurationItem\"h\n\x17SignedConfigurationItem\x12\x19\n\x11\x43onfigurationItem\x18\x01 \x01(\x0c\x12\x32\n\nSignatures\x18\x02 \x03(\x0b\x32\x1e.common.ConfigurationSignature\"\x86\x02\n\x11\x43onfigurationItem\x12#\n\x06Header\x18\x01 \x01(\x0b\x32\x13.common.ChainHeader\x12\x39\n\x04Type\x18\x02 \x01(\x0e\x32+.common.ConfigurationItem.ConfigurationType\x12\x14\n\x0cLastModified\x18\x03 \x01(\x04\x12\x1a\n\x12ModificationPolicy\x18\x04 \x01(\t\x12\x0b\n\x03Key\x18\x05 \x01(\t\x12\r\n\x05Value\x18\x06 \x01(\x0c\"C\n\x11\x43onfigurationType\x12\n\n\x06Policy\x10\x00\x12\t\n\x05\x43hain\x10\x01\x12\x0b\n\x07Orderer\x10\x02\x12\n\n\x06\x46\x61\x62ric\x10\x03\"D\n\x16\x43onfigurationSignature\x12\x17\n\x0fsignatureHeader\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"L\n\x06Policy\x12:\n\x0fSignaturePolicy\x18\x01 \x01(\x0b\x32\x1f.common.SignaturePolicyEnvelopeH\x00\x42\x06\n\x04Type\"g\n\x17SignaturePolicyEnvelope\x12\x0f\n\x07Version\x18\x01 \x01(\x05\x12\'\n\x06Policy\x18\x02 \x01(\x0b\x32\x17.common.SignaturePolicy\x12\x12\n\nIdentities\x18\x03 \x03(\x0c\"\x9d\x01\n\x0fSignaturePolicy\x12\x12\n\x08SignedBy\x18\x01 \x01(\x05H\x00\x12.\n\x04\x46rom\x18\x02 \x01(\x0b\x32\x1e.common.SignaturePolicy.NOutOfH\x00\x1a>\n\x06NOutOf\x12\t\n\x01N\x18\x01 \x01(\x05\x12)\n\x08Policies\x18\x02 \x03(\x0b\x32\x17.common.SignaturePolicyB\x06\n\x04TypeB-Z+github.com/hyperledger/fabric/protos/commonb\x06proto3') + serialized_pb=_b('\n\x1a\x63ommon/configuration.proto\x12\x06\x63ommon\x1a\x13\x63ommon/common.proto\"G\n\x15\x43onfigurationEnvelope\x12.\n\x05Items\x18\x01 \x03(\x0b\x32\x1f.common.SignedConfigurationItem\"h\n\x17SignedConfigurationItem\x12\x19\n\x11\x43onfigurationItem\x18\x01 \x01(\x0c\x12\x32\n\nSignatures\x18\x02 \x03(\x0b\x32\x1e.common.ConfigurationSignature\"\x86\x02\n\x11\x43onfigurationItem\x12#\n\x06Header\x18\x01 \x01(\x0b\x32\x13.common.ChainHeader\x12\x39\n\x04Type\x18\x02 \x01(\x0e\x32+.common.ConfigurationItem.ConfigurationType\x12\x14\n\x0cLastModified\x18\x03 \x01(\x04\x12\x1a\n\x12ModificationPolicy\x18\x04 \x01(\t\x12\x0b\n\x03Key\x18\x05 \x01(\t\x12\r\n\x05Value\x18\x06 \x01(\x0c\"C\n\x11\x43onfigurationType\x12\n\n\x06Policy\x10\x00\x12\t\n\x05\x43hain\x10\x01\x12\x0b\n\x07Orderer\x10\x02\x12\n\n\x06\x46\x61\x62ric\x10\x03\"D\n\x16\x43onfigurationSignature\x12\x17\n\x0fsignatureHeader\x18\x01 \x01(\x0c\x12\x11\n\tsignature\x18\x02 \x01(\x0c\"\'\n\x0f\x43\x65rtificateList\x12\x14\n\x0c\x43\x65rtificates\x18\x01 \x03(\x0c\"L\n\x06Policy\x12:\n\x0fSignaturePolicy\x18\x01 \x01(\x0b\x32\x1f.common.SignaturePolicyEnvelopeH\x00\x42\x06\n\x04Type\"g\n\x17SignaturePolicyEnvelope\x12\x0f\n\x07Version\x18\x01 \x01(\x05\x12\'\n\x06Policy\x18\x02 \x01(\x0b\x32\x17.common.SignaturePolicy\x12\x12\n\nIdentities\x18\x03 \x03(\x0c\"\x9d\x01\n\x0fSignaturePolicy\x12\x12\n\x08SignedBy\x18\x01 \x01(\x05H\x00\x12.\n\x04\x46rom\x18\x02 \x01(\x0b\x32\x1e.common.SignaturePolicy.NOutOfH\x00\x1a>\n\x06NOutOf\x12\t\n\x01N\x18\x01 \x01(\x05\x12)\n\x08Policies\x18\x02 \x03(\x0b\x32\x17.common.SignaturePolicyB\x06\n\x04TypeB-Z+github.com/hyperledger/fabric/protos/commonb\x06proto3') , dependencies=[common_dot_common__pb2.DESCRIPTOR,]) _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -232,6 +232,37 @@ ) +_CERTIFICATELIST = _descriptor.Descriptor( + name='CertificateList', + full_name='common.CertificateList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Certificates', full_name='common.CertificateList.Certificates', index=0, + number=1, type=12, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=573, + serialized_end=612, +) + + _POLICY = _descriptor.Descriptor( name='Policy', full_name='common.Policy', @@ -261,8 +292,8 @@ name='Type', full_name='common.Policy.Type', index=0, containing_type=None, fields=[]), ], - serialized_start=573, - serialized_end=649, + serialized_start=614, + serialized_end=690, ) @@ -306,8 +337,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=651, - serialized_end=754, + serialized_start=692, + serialized_end=795, ) @@ -344,8 +375,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=844, - serialized_end=906, + serialized_start=885, + serialized_end=947, ) _SIGNATUREPOLICY = _descriptor.Descriptor( @@ -384,8 +415,8 @@ name='Type', full_name='common.SignaturePolicy.Type', index=0, containing_type=None, fields=[]), ], - serialized_start=757, - serialized_end=914, + serialized_start=798, + serialized_end=955, ) _CONFIGURATIONENVELOPE.fields_by_name['Items'].message_type = _SIGNEDCONFIGURATIONITEM @@ -411,6 +442,7 @@ DESCRIPTOR.message_types_by_name['SignedConfigurationItem'] = _SIGNEDCONFIGURATIONITEM DESCRIPTOR.message_types_by_name['ConfigurationItem'] = _CONFIGURATIONITEM DESCRIPTOR.message_types_by_name['ConfigurationSignature'] = _CONFIGURATIONSIGNATURE +DESCRIPTOR.message_types_by_name['CertificateList'] = _CERTIFICATELIST DESCRIPTOR.message_types_by_name['Policy'] = _POLICY DESCRIPTOR.message_types_by_name['SignaturePolicyEnvelope'] = _SIGNATUREPOLICYENVELOPE DESCRIPTOR.message_types_by_name['SignaturePolicy'] = _SIGNATUREPOLICY @@ -443,6 +475,13 @@ )) _sym_db.RegisterMessage(ConfigurationSignature) +CertificateList = _reflection.GeneratedProtocolMessageType('CertificateList', (_message.Message,), dict( + DESCRIPTOR = _CERTIFICATELIST, + __module__ = 'common.configuration_pb2' + # @@protoc_insertion_point(class_scope:common.CertificateList) + )) +_sym_db.RegisterMessage(CertificateList) + Policy = _reflection.GeneratedProtocolMessageType('Policy', (_message.Message,), dict( DESCRIPTOR = _POLICY, __module__ = 'common.configuration_pb2' diff --git a/bddtests/compose-defaults.yml b/bddtests/compose-defaults.yml index b5e5f14df61..324f46527bf 100644 --- a/bddtests/compose-defaults.yml +++ b/bddtests/compose-defaults.yml @@ -1,20 +1,24 @@ -vp: - image: hyperledger/fabric-peer - environment: - - CORE_PEER_ADDRESSAUTODETECT=true - - CORE_VM_ENDPOINT=http://172.17.0.1:2375 - # 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} - # 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 - #command: sh -c "exec $$GOPATH/src/github.com/hyperledger/fabric/bddtests/scripts/start-peer.sh" - command: sh -c "sleep 2; peer node start" +version: "2" - # Use these options if coverage desired for peers - #image: hyperledger/fabric-peer-coverage - #command: ./peer.test --test.coverprofile=coverage.cov node start -membersrvc: - image: hyperledger/fabric-membersrvc - command: membersrvc +services: + + vp: + image: hyperledger/fabric-peer + environment: + - CORE_PEER_ADDRESSAUTODETECT=true + - CORE_VM_ENDPOINT=http://172.17.0.1:2375 + # 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} + # 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 + #command: sh -c "exec $$GOPATH/src/github.com/hyperledger/fabric/bddtests/scripts/start-peer.sh" + command: sh -c "sleep 2; peer node start" + + # Use these options if coverage desired for peers + #image: hyperledger/fabric-peer-coverage + #command: ./peer.test --test.coverprofile=coverage.cov node start + membersrvc: + image: hyperledger/fabric-membersrvc + command: membersrvc diff --git a/bddtests/context.go b/bddtests/context.go index fbe3dcb3715..116fd034985 100644 --- a/bddtests/context.go +++ b/bddtests/context.go @@ -23,6 +23,7 @@ import ( "github.com/DATA-DOG/godog" "github.com/DATA-DOG/godog/gherkin" + "github.com/hyperledger/fabric/core/util" ) // BDDContext represents the current context for the executing scenario. Commensurate concept of 'context' from behave testing. @@ -88,3 +89,40 @@ func (b *BDDContext) GetArgsForUser(cells []*gherkin.TableCell, userRegistration } return args, nil } + +func (b *BDDContext) weCompose(composeFiles string) error { + if b.composition != nil { + return fmt.Errorf("Already have composition in BDD context (%s)", b.composition.projectName) + } + // Need a unique name, but docker does not allow '-' in names + composeProjectName := strings.Replace(util.GenerateUUID(), "-", "", -1) + newComposition, err := NewComposition(composeProjectName, composeFiles) + if err != nil { + return fmt.Errorf("Error composing system in BDD context: %s", err) + } + b.composition = newComposition + return nil +} + +func (b *BDDContext) beforeScenario(scenarioOrScenarioOutline interface{}) { + b.scenarioOrScenarioOutline = scenarioOrScenarioOutline + //switch t := scenarioOrScenarioOutline.(type) { + //case *gherkin.Scenario: + // fmt.Printf("Scenario recieved %v", t) + //case *gherkin.ScenarioOutline: + // fmt.Printf("ScenarioOutline recieved %v", t) + //} +} + +func (b *BDDContext) afterScenarioDecompose(interface{}, error) { + if b.hasTag("@doNotDecompose") == true { + fmt.Printf("Not decomposing: %s", b.getScenarioDefinition().Name) + } else { + if b.composition != nil { + b.composition.Decompose() + } + } + // Now clear the users + b.composition = nil + b.users = make(map[string]*UserRegistration) +} diff --git a/bddtests/context_bootstrap.go b/bddtests/context_bootstrap.go new file mode 100644 index 00000000000..59a6706598d --- /dev/null +++ b/bddtests/context_bootstrap.go @@ -0,0 +1,43 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package bddtests + +import ( + "github.com/DATA-DOG/godog" + "github.com/DATA-DOG/godog/gherkin" +) + +func (b *BDDContext) theOrdererNetworkHasOrganizations(arg1 *gherkin.DataTable) error { + return godog.ErrPending +} + +// FeatureContextBootstrap setup the FeatureContext for bootstrap steps +func FeatureContextBootstrap(bddCtx *BDDContext, s *godog.Suite) { + + s.Step(`^the orderer network has organizations:$`, bddCtx.theOrdererNetworkHasOrganizations) + //s.Step(`^user requests role of orderer admin by creating a key and csr for orderer and acquires signed certificate from organization:$`, userRequestsRoleOfOrdererAdminByCreatingAKeyAndCsrForOrdererAndAcquiresSignedCertificateFromOrganization) + //s.Step(`^the peer network has organizations:$`, thePeerNetworkHasOrganizations) + //s.Step(`^a chainBootstrapAdmin is identified and given access to all public certificates and orderer node info$`, aChainBootstrapAdminIsIdentifiedAndGivenAccessToAllPublicCertificatesAndOrdererNodeInfo) + //s.Step(`^the chainBootstrapAdmin creates the genesis block for chain \'chain(\d+)\' for network config policy \'unanimous\' and consensus \'solo\' and peer organizations:$`, theChainBootstrapAdminCreatesTheGenesisBlockForChainChainForNetworkConfigPolicyUnanimousAndConsensusSoloAndPeerOrganizations) + //s.Step(`^the orderer admins inspect and approve the genesis block for chain \'chain(\d+)\'$`, theOrdererAdminsInspectAndApproveTheGenesisBlockForChainChain) + //s.Step(`^the orderer admins use the genesis block for chain \'chain(\d+)\' to configure orderers$`, theOrdererAdminsUseTheGenesisBlockForChainChainToConfigureOrderers) + //s.Step(`^user requests role of peer admin by creating a key and csr for peer and acquires signed certificate from organization:$`, userRequestsRoleOfPeerAdminByCreatingAKeyAndCsrForPeerAndAcquiresSignedCertificateFromOrganization) + //s.Step(`^peer admins get the genesis block for chain \'chain(\d+)\' from chainBoostrapAdmin$`, peerAdminsGetTheGenesisBlockForChainChainFromChainBoostrapAdmin) + //s.Step(`^the peer admins inspect and approve the genesis block for chain \'chain(\d+)\'$`, thePeerAdminsInspectAndApproveTheGenesisBlockForChainChain) + //s.Step(`^the peer admins use the genesis block for chain \'chain(\d+)\' to configure peers$`, thePeerAdminsUseTheGenesisBlockForChainChainToConfigurePeers) + //s.Step(`^user "([^"]*)" registers with peer organization "([^"]*)"$`, userRegistersWithPeerOrganization) +} diff --git a/bddtests/endorser_test.go b/bddtests/context_endorser.go similarity index 80% rename from bddtests/endorser_test.go rename to bddtests/context_endorser.go index d165b3d9443..fcce8072798 100644 --- a/bddtests/endorser_test.go +++ b/bddtests/context_endorser.go @@ -22,8 +22,6 @@ import ( "strconv" "time" - "strings" - "github.com/DATA-DOG/godog" "github.com/DATA-DOG/godog/gherkin" "github.com/hyperledger/fabric/core/chaincode/platforms" @@ -69,19 +67,6 @@ func (b *BDDContext) build(spec *pb.ChaincodeSpec) (*pb.ChaincodeDeploymentSpec, chaincodeDeploymentSpec := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackageBytes} return chaincodeDeploymentSpec, nil } -func (b *BDDContext) weCompose(composeFiles string) error { - if b.composition != nil { - return fmt.Errorf("Already have composition in BDD context (%s)", b.composition.projectName) - } - // Need a unique name, but docker does not allow '-' in names - composeProjectName := strings.Replace(util.GenerateUUID(), "-", "", -1) - newComposition, err := NewComposition(composeProjectName, composeFiles) - if err != nil { - return fmt.Errorf("Error composing system in BDD context: %s", err) - } - b.composition = newComposition - return nil -} func (b *BDDContext) requestingFrom(arg1, arg2 string) error { return godog.ErrPending @@ -368,47 +353,3 @@ func (b *BDDContext) userSetsESCCToForChaincodeSpec(arg1, arg2, arg3 string) err func (b *BDDContext) userSetsVSCCToForChaincodeSpec(arg1, arg2, arg3 string) error { return godog.ErrPending } - -func (b *BDDContext) beforeScenario(scenarioOrScenarioOutline interface{}) { - b.scenarioOrScenarioOutline = scenarioOrScenarioOutline - //switch t := scenarioOrScenarioOutline.(type) { - //case *gherkin.Scenario: - // fmt.Printf("Scenario recieved %v", t) - //case *gherkin.ScenarioOutline: - // fmt.Printf("ScenarioOutline recieved %v", t) - //} -} - -func (b *BDDContext) afterScenarioDecompose(interface{}, error) { - if b.hasTag("@doNotDecompose") == true { - fmt.Printf("Not decomposing: %s", b.getScenarioDefinition().Name) - } else { - b.composition.Decompose() - } - // Now clear the users - b.composition = nil - b.users = make(map[string]*UserRegistration) -} - -func FeatureContext(s *godog.Suite) { - bddCtx := &BDDContext{godogSuite: s, users: make(map[string]*UserRegistration), grpcClientPort: 7051} - - s.BeforeScenario(bddCtx.beforeScenario) - s.AfterScenario(bddCtx.afterScenarioDecompose) - s.Step(`^we compose "([^"]*)"$`, bddCtx.weCompose) - s.Step(`^requesting "([^"]*)" from "([^"]*)"$`, bddCtx.requestingFrom) - s.Step(`^I should get a JSON response with array "([^"]*)" contains "([^"]*)" elements$`, bddCtx.iShouldGetAJSONResponseWithArrayContainsElements) - s.Step(`^I wait "([^"]*)" seconds$`, bddCtx.iWaitSeconds) - s.Step(`^I register with CA supplying username "([^"]*)" and secret "([^"]*)" on peers:$`, bddCtx.iRegisterWithCASupplyingUsernameAndSecretOnPeers) - s.Step(`^user "([^"]*)" creates a chaincode spec "([^"]*)" of type "([^"]*)" for chaincode "([^"]*)" with args$`, bddCtx.userCreatesAChaincodeSpecOfTypeForChaincodeWithArgs) - - s.Step(`^user "([^"]*)" creates a deployment spec "([^"]*)" using chaincode spec "([^"]*)" and devops on peer "([^"]*)"$`, bddCtx.userCreatesADeploymentSpecUsingChaincodeSpecAndDevopsOnPeer) - //s.Step(`^user "([^"]*)" creates a deployment spec "([^"]*)" using chaincode spec "([^"]*)"$`, bddCtx.userCreatesADeploymentSpecUsingChaincodeSpec) - - s.Step(`^user "([^"]*)" creates a deployment proposal "([^"]*)" using chaincode deployment spec "([^"]*)"$`, bddCtx.userCreatesADeploymentProposalUsingChaincodeDeploymentSpec) - s.Step(`^user "([^"]*)" sends proposal "([^"]*)" to endorsers with timeout of "([^"]*)" seconds:$`, bddCtx.userSendsProposalToEndorsersWithTimeoutOfSeconds) - s.Step(`^user "([^"]*)" stores their last result as "([^"]*)"$`, bddCtx.userStoresTheirLastResultAs) - s.Step(`^user "([^"]*)" expects proposal responses "([^"]*)" with status "([^"]*)" from endorsers:$`, bddCtx.userExpectsProposalResponsesWithStatusFromEndorsers) - s.Step(`^user "([^"]*)" sets ESCC to "([^"]*)" for chaincode spec "([^"]*)"$`, bddCtx.userSetsESCCToForChaincodeSpec) - s.Step(`^user "([^"]*)" sets VSCC to "([^"]*)" for chaincode spec "([^"]*)"$`, bddCtx.userSetsVSCCToForChaincodeSpec) -} diff --git a/bddtests/docker-compose-next-4.yml b/bddtests/docker-compose-next-4.yml index 8604bb4929d..5ca5886a82f 100644 --- a/bddtests/docker-compose-next-4.yml +++ b/bddtests/docker-compose-next-4.yml @@ -1,68 +1,76 @@ -membersrvc0: - extends: - file: compose-defaults.yml - service: membersrvc +version: "2" -orderer0: - extends: - file: docker-compose-orderer-solo.yml - service: orderer0 +services: + membersrvc0: + extends: + file: compose-defaults.yml + service: membersrvc -vp0: - extends: - file: docker-compose-next.yml - service: vpNext - environment: - - CORE_PEER_ID=vp0 - - CORE_SECURITY_ENROLLID=test_vp0 - - CORE_SECURITY_ENROLLSECRET=MwYpmSRjupbT - - CORE_PEER_PROFILE_ENABLED=true - links: - - membersrvc0 - - orderer0 - # ports: - # - 7050:6060 + orderer0: + extends: + file: docker-compose-orderer-solo.yml + service: orderer0 -vp1: - extends: - file: docker-compose-next.yml - service: vpNext - environment: - - CORE_PEER_ID=vp1 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 - - CORE_SECURITY_ENROLLID=test_vp1 - - CORE_SECURITY_ENROLLSECRET=5wgHK9qqYaPy - links: - - membersrvc0 - - orderer0 - - vp0 + vp0: + extends: + file: docker-compose-next.yml + service: vpNext + environment: + - 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 + depends_on: + - membersrvc0 + - orderer0 + # ports: + # - 7050:6060 -vp2: - extends: - file: docker-compose-next.yml - service: vpNext - environment: - - CORE_PEER_ID=vp2 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 - - CORE_SECURITY_ENROLLID=test_vp2 - - CORE_SECURITY_ENROLLSECRET=vQelbRvja7cJ - links: - - membersrvc0 - - orderer0 - - vp0 -vp3: - extends: - file: docker-compose-next.yml - service: vpNext - environment: - - CORE_PEER_ID=vp3 - - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 - - CORE_SECURITY_ENROLLID=test_vp3 - - CORE_SECURITY_ENROLLSECRET=9LKqKH5peurL - links: - - membersrvc0 - - orderer0 - - vp0 + vp1: + extends: + file: docker-compose-next.yml + service: vpNext + environment: + - CORE_PEER_ID=vp1 + - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 + - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 + - CORE_SECURITY_ENROLLID=test_vp1 + - CORE_SECURITY_ENROLLSECRET=5wgHK9qqYaPy + depends_on: + - membersrvc0 + - orderer0 + - vp0 + + vp2: + extends: + file: docker-compose-next.yml + service: vpNext + environment: + - CORE_PEER_ID=vp2 + - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 + - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 + - CORE_SECURITY_ENROLLID=test_vp2 + - CORE_SECURITY_ENROLLSECRET=vQelbRvja7cJ + depends_on: + - membersrvc0 + - orderer0 + - vp0 + + vp3: + extends: + file: docker-compose-next.yml + service: vpNext + environment: + - CORE_PEER_ID=vp3 + - CORE_PEER_DISCOVERY_ROOTNODE=vp0:7051 + - CORE_PEER_COMMITTER_LEDGER_ORDERER=orderer0:7050 + - CORE_SECURITY_ENROLLID=test_vp3 + - CORE_SECURITY_ENROLLSECRET=9LKqKH5peurL + depends_on: + - membersrvc0 + - orderer0 + - vp0 diff --git a/bddtests/docker-compose-next.yml b/bddtests/docker-compose-next.yml index 840e9631e31..9745093538f 100644 --- a/bddtests/docker-compose-next.yml +++ b/bddtests/docker-compose-next.yml @@ -1,13 +1,17 @@ -vpNext: - extends: - file: compose-defaults.yml - service: vp - environment: - - CORE_NEXT=true - - CORE_PEER_ENDORSER_ENABLED=true - - CORE_SECURITY_ENABLED=true - - CORE_PEER_PKI_ECA_PADDR=membersrvc0:7054 - - CORE_PEER_PKI_TCA_PADDR=membersrvc0:7054 - - CORE_PEER_PKI_TLSCA_PADDR=membersrvc0:7054 - - CORE_PEER_PKI_TLS_ROOTCERT_FILE=./bddtests/tlsca.cert +version: "2" + +services: + + vpNext: + extends: + file: compose-defaults.yml + service: vp + environment: + - CORE_NEXT=true + - CORE_PEER_ENDORSER_ENABLED=true + - CORE_SECURITY_ENABLED=true + - CORE_PEER_PKI_ECA_PADDR=membersrvc0:7054 + - CORE_PEER_PKI_TCA_PADDR=membersrvc0:7054 + - CORE_PEER_PKI_TLSCA_PADDR=membersrvc0:7054 + - CORE_PEER_PKI_TLS_ROOTCERT_FILE=./bddtests/tlsca.cert diff --git a/bddtests/docker-compose-orderer-base.yml b/bddtests/docker-compose-orderer-base.yml index 40dba098738..2764c3d8bbd 100644 --- a/bddtests/docker-compose-orderer-base.yml +++ b/bddtests/docker-compose-orderer-base.yml @@ -1,11 +1,22 @@ -ordererBase: - image: hyperledger/fabric-orderer - environment: - - ORDERER_GENERAL_LEDGERTYPE=ram - - ORDERER_GENERAL_BATCHTIMEOUT=10s - - ORDERER_GENERAL_BATCHSIZE=10 - - ORDERER_GENERAL_MAXWINDOWSIZE=1000 - - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 - - ORDERER_RAMLEDGER_HISTORY_SIZE=100 - working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer - command: orderer +version: "2" + +services: + + ordererBase: + image: hyperledger/fabric-orderer + environment: + - ORDERER_GENERAL_LEDGERTYPE=ram + - ORDERER_GENERAL_BATCHTIMEOUT=10s + - ORDERER_GENERAL_BATCHSIZE=10 + - ORDERER_GENERAL_MAXWINDOWSIZE=1000 + - ORDERER_GENERAL_LISTENADDRESS=0.0.0.0 + - ORDERER_GENERAL_GENESISIFILE=${ORDERER_GENERAL_GENESISIFILE} + - ORDERER_RAMLEDGER_HISTORY_SIZE=100 + volumes: + - ./volumes/orderer:/var/hyperledger/bddtests/volumes/orderer + working_dir: /opt/gopath/src/github.com/hyperledger/fabric/orderer + command: orderer + +volumes: + + orderer: diff --git a/bddtests/docker-compose-orderer-solo.yml b/bddtests/docker-compose-orderer-solo.yml index 3f2b7b5c70d..53477032d97 100644 --- a/bddtests/docker-compose-orderer-solo.yml +++ b/bddtests/docker-compose-orderer-solo.yml @@ -1,6 +1,10 @@ -orderer0: - extends: - file: docker-compose-orderer-base.yml - service: ordererBase - environment: - - ORDERER_GENERAL_ORDERERTYPE=solo +version: '2' + +services: + + orderer0: + extends: + file: docker-compose-orderer-base.yml + service: ordererBase + environment: + - ORDERER_GENERAL_ORDERERTYPE=solo diff --git a/bddtests/features/bootstrap.feature b/bddtests/features/bootstrap.feature new file mode 100644 index 00000000000..a5f13b051b0 --- /dev/null +++ b/bddtests/features/bootstrap.feature @@ -0,0 +1,75 @@ +# +# Test Bootstrap function +# +# Tags that can be used and will affect test internals: +# @doNotDecompose will NOT decompose the named compose_yaml after scenario ends. Useful for setting up environment and reviewing after scenario. +# @chaincodeImagesUpToDate use this if all scenarios chaincode images are up to date, and do NOT require building. BE SURE!!! + +#@chaincodeImagesUpToDate +@bootstrap +Feature: Bootstrap + As a blockchain entrepreneur + I want to bootstrap a new blockchain network + +# @doNotDecompose + Scenario Outline: Bootstrap a development network with 1 peer and 1 orderer, each having a single independent root of trust (No COP) + #creates 1 self-signed key/cert pair per orderer organization with an associated admin + Given the orderer network has organizations: + | Organization | + | ordererOrg0 | + And user requests role of orderer admin by creating a key and csr for orderer and acquires signed certificate from organization: + | User | Orderer | Organization | + | orderer0Admin | orderer0 | ordererOrg0 | + | orderer0Signer | orderer0 | ordererOrg0 | +# And user requests role of orderer signer by creating a key and csr for orderer and acquires signed certificate from organization: +# | User | Orderer | Organization | + + And the peer network has organizations: + | 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 + # Only the peer organizations can vary. + And the ordererBootstrapAdmin creates the genesis block for chain "" for network config policy "" and consensus "" and peer organizations: + | peerOrg0 | + + And the orderer admins inspect and approve the genesis block for chain "" + + # 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 | + + + 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' + + And the peer admins use the genesis block for chain 'chain1' to configure peers + + And I wait "1" seconds + And user "binhn" registers with peer organization "peerOrg0" + + When user "binhn" creates a chaincode spec "cc_spec" of type "GOLANG" for chaincode "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02" with args + | funcName | arg1 | arg2 | arg3 | arg4 | + | init | a | 100 | b | 200 | + And user "binhn" creates a deployment spec "cc_deploy_spec" using chaincode spec "cc_spec" and devops on peer "vp0" + And user "binhn" creates a deployment proposal "proposal1" using chaincode deployment spec "cc_deploy_spec" + And user "binhn" sends proposal "proposal1" to endorsers with timeout of "20" seconds: + | peer0 | + And user "binhn" stores their last result as "proposal1Responses" + Then user "binhn" expects proposal responses "proposal1Responses" with status "200" from endorsers: + | peer0 | + + + Examples: Orderer Options + | ComposeFile | Waittime | OrdererSystemChainId |PolicyType | ConsensusType | + | docker-compose-next-4.yml | 60 | **TEST_CHAINID** |unanimous | solo | diff --git a/bddtests/orderer/configuration_pb2.py b/bddtests/orderer/configuration_pb2.py new file mode 100644 index 00000000000..f3b101e93df --- /dev/null +++ b/bddtests/orderer/configuration_pb2.py @@ -0,0 +1,202 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: orderer/configuration.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from common import common_pb2 as common_dot_common__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='orderer/configuration.proto', + package='orderer', + syntax='proto3', + serialized_pb=_b('\n\x1borderer/configuration.proto\x12\x07orderer\x1a\x13\x63ommon/common.proto\"\x1d\n\rConsensusType\x12\x0c\n\x04type\x18\x01 \x01(\t\"\x1d\n\tBatchSize\x12\x10\n\x08messages\x18\x01 \x01(\x05\"0\n\x0e\x43reationPolicy\x12\x0e\n\x06policy\x18\x01 \x01(\t\x12\x0e\n\x06\x64igest\x18\x02 \x01(\x0c\"!\n\rChainCreators\x12\x10\n\x08policies\x18\x01 \x03(\tB.Z,github.com/hyperledger/fabric/protos/ordererb\x06proto3') + , + dependencies=[common_dot_common__pb2.DESCRIPTOR,]) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_CONSENSUSTYPE = _descriptor.Descriptor( + name='ConsensusType', + full_name='orderer.ConsensusType', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='type', full_name='orderer.ConsensusType.type', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=61, + serialized_end=90, +) + + +_BATCHSIZE = _descriptor.Descriptor( + name='BatchSize', + full_name='orderer.BatchSize', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='messages', full_name='orderer.BatchSize.messages', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=92, + serialized_end=121, +) + + +_CREATIONPOLICY = _descriptor.Descriptor( + name='CreationPolicy', + full_name='orderer.CreationPolicy', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='policy', full_name='orderer.CreationPolicy.policy', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='digest', full_name='orderer.CreationPolicy.digest', index=1, + number=2, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=123, + serialized_end=171, +) + + +_CHAINCREATORS = _descriptor.Descriptor( + name='ChainCreators', + full_name='orderer.ChainCreators', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='policies', full_name='orderer.ChainCreators.policies', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=173, + serialized_end=206, +) + +DESCRIPTOR.message_types_by_name['ConsensusType'] = _CONSENSUSTYPE +DESCRIPTOR.message_types_by_name['BatchSize'] = _BATCHSIZE +DESCRIPTOR.message_types_by_name['CreationPolicy'] = _CREATIONPOLICY +DESCRIPTOR.message_types_by_name['ChainCreators'] = _CHAINCREATORS + +ConsensusType = _reflection.GeneratedProtocolMessageType('ConsensusType', (_message.Message,), dict( + DESCRIPTOR = _CONSENSUSTYPE, + __module__ = 'orderer.configuration_pb2' + # @@protoc_insertion_point(class_scope:orderer.ConsensusType) + )) +_sym_db.RegisterMessage(ConsensusType) + +BatchSize = _reflection.GeneratedProtocolMessageType('BatchSize', (_message.Message,), dict( + DESCRIPTOR = _BATCHSIZE, + __module__ = 'orderer.configuration_pb2' + # @@protoc_insertion_point(class_scope:orderer.BatchSize) + )) +_sym_db.RegisterMessage(BatchSize) + +CreationPolicy = _reflection.GeneratedProtocolMessageType('CreationPolicy', (_message.Message,), dict( + DESCRIPTOR = _CREATIONPOLICY, + __module__ = 'orderer.configuration_pb2' + # @@protoc_insertion_point(class_scope:orderer.CreationPolicy) + )) +_sym_db.RegisterMessage(CreationPolicy) + +ChainCreators = _reflection.GeneratedProtocolMessageType('ChainCreators', (_message.Message,), dict( + DESCRIPTOR = _CHAINCREATORS, + __module__ = 'orderer.configuration_pb2' + # @@protoc_insertion_point(class_scope:orderer.ChainCreators) + )) +_sym_db.RegisterMessage(ChainCreators) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('Z,github.com/hyperledger/fabric/protos/orderer')) +import grpc +from grpc.beta import implementations as beta_implementations +from grpc.beta import interfaces as beta_interfaces +from grpc.framework.common import cardinality +from grpc.framework.interfaces.face import utilities as face_utilities +# @@protoc_insertion_point(module_scope) diff --git a/bddtests/steps/bootstrap_impl.py b/bddtests/steps/bootstrap_impl.py new file mode 100644 index 00000000000..e67ea3e58a2 --- /dev/null +++ b/bddtests/steps/bootstrap_impl.py @@ -0,0 +1,61 @@ +# Copyright IBM Corp. 2016 All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +import bdd_test_util +import bootstrap_util + +@given(u'the orderer network has organizations') +def step_impl(context): + assert 'table' in context, "Expected table of orderer organizations" + directory = bootstrap_util.getDirectory(context) + for row in context.table.rows: + org = directory.getOrganization(row['Organization'], shouldCreate = True) + org.addToNetwork(bootstrap_util.Network.Orderer) + + +@given(u'user requests role of orderer admin by creating a key and csr for orderer and acquires signed certificate from organization') +def step_impl(context): + assert 'table' in context, "Expected table with triplet of User/Orderer/Organization" + directory = bootstrap_util.getDirectory(context) + for row in context.table.rows: + directory.registerOrdererAdminTuple(row['User'], row['Orderer'], row['Organization']) + +@given(u'the peer network has organizations') +def step_impl(context): + assert 'table' in context, "Expected table of peer network organizations" + directory = bootstrap_util.getDirectory(context) + for row in context.table.rows: + org = directory.getOrganization(row['Organization'], shouldCreate = True) + org.addToNetwork(bootstrap_util.Network.Peer) + +@given(u'a ordererBootstrapAdmin is identified and given access to all public certificates and orderer node info') +def step_impl(context): + directory = bootstrap_util.getDirectory(context) + assert len(directory.ordererAdminTuples) > 0, "No orderer admin tuples defined!!!" + +@given(u'the ordererBootstrapAdmin creates the genesis block for chain "{ordererSystemChainId}" for network config policy "{networkConfigPolicy}" and consensus "{consensusType}" and peer organizations') +def step_impl(context, ordererSystemChainId, networkConfigPolicy, consensusType): + genesisBlock = bootstrap_util.createGenesisBlock(context, ordererSystemChainId, networkConfigPolicy, consensusType) + bootstrap_util.OrdererGensisBlockCompositionCallback(context, genesisBlock) + +@given(u'the orderer admins inspect and approve the genesis block for chain "{chainId}"') +def step_impl(context, chainId): + pass + +@given(u'the orderer admins use the genesis block for chain "{chainId}" to configure orderers') +def step_impl(context, chainId): + pass + #raise NotImplementedError(u'STEP: Given the orderer admins use the genesis block for chain "**TEST_CHAINID**" to configure orderers') \ No newline at end of file diff --git a/bddtests/steps/bootstrap_util.py b/bddtests/steps/bootstrap_util.py new file mode 100644 index 00000000000..faae4f4036a --- /dev/null +++ b/bddtests/steps/bootstrap_util.py @@ -0,0 +1,421 @@ +# Copyright IBM Corp. 2016 All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import time +import sys +import hashlib +if sys.version_info < (3, 6): + import sha3 + +from OpenSSL import crypto +from OpenSSL import rand +from collections import namedtuple +from enum import Enum + +from google.protobuf import timestamp_pb2 +from common import common_pb2 as common_dot_common_pb2 +from common import configuration_pb2 as common_dot_configuration_pb2 +# import orderer +from orderer import configuration_pb2 as orderer_dot_configuration_pb2 + +import os +import shutil +import compose + +# Type to represent tuple of user, nodeName, ogranization +NodeAdminTuple = namedtuple("NodeAdminTuple", ['user', 'nodeName', 'organization']) + + + +def createKey(): + #Create RSA key, 2048 bit + pk = crypto.PKey() + pk.generate_key(crypto.TYPE_RSA,2048) + assert pk.check()==True + return pk + +def computeCryptoHash(data): + s = hashlib.sha3_256() + s.update(data) + return s.hexdigest() + + +def createCertRequest(pkey, digest="sha256", **name): + """ + Create a certificate request. + Arguments: pkey - The key to associate with the request + digest - Digestion method to use for signing, default is sha256 + **name - The name of the subject of the request, possible + arguments are: + C - Country name + ST - State or province name + L - Locality name + O - Organization name + OU - Organizational unit name + CN - Common name + emailAddress - E-mail address + Returns: The certificate request in an X509Req object + """ + req = crypto.X509Req() + subj = req.get_subject() + + for key, value in name.items(): + setattr(subj, key, value) + + req.set_pubkey(pkey) + req.sign(pkey, digest) + return req + +def createCertificate(req, issuerCertKey, serial, validityPeriod, digest="sha256"): + """ + Generate a certificate given a certificate request. + Arguments: req - Certificate request to use + issuerCert - The certificate of the issuer + issuerKey - The private key of the issuer + serial - Serial number for the certificate + notBefore - Timestamp (relative to now) when the certificate + starts being valid + notAfter - Timestamp (relative to now) when the certificate + stops being valid + digest - Digest method to use for signing, default is sha256 + Returns: The signed certificate in an X509 object + """ + issuerCert, issuerKey = issuerCertKey + notBefore, notAfter = validityPeriod + cert = crypto.X509() + cert.set_serial_number(serial) + cert.gmtime_adj_notBefore(notBefore) + cert.gmtime_adj_notAfter(notAfter) + cert.set_issuer(issuerCert.get_subject()) + cert.set_subject(req.get_subject()) + cert.set_pubkey(req.get_pubkey()) + cert.sign(issuerKey, digest) + return cert + +#SUBJECT_DEFAULT = {countryName : "US", stateOrProvinceName : "NC", localityName : "RTP", organizationName : "IBM", organizationalUnitName : "Blockchain"} + +class Entity: + + def __init__(self, name): + self.name = name + self.pKey = createKey() + + def createCertRequest(self, nodeName): + req = createCertRequest(self.pKey, CN=nodeName) + print("request => {0}".format(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))) + return req + + + +class User(Entity): + def __init__(self, name): + Entity.__init__(self, name) + +class Network(Enum): + Orderer = 1 + Peer = 2 + +class Organization(Entity): + + def __init__(self, name): + Entity.__init__(self, name) + req = createCertRequest(self.pKey, CN=name) + numYrs = 1 + self.signedCert = createCertificate(req, (req, self.pKey), 1000, (0, 60*60*24*365*numYrs)) + # Which networks this organization belongs to + self.networks = [] + + def createCertificate(self, certReq): + numYrs = 1 + return createCertificate(certReq, (self.signedCert, self.pKey), 1000, (0, 60*60*24*365*numYrs)) + + def addToNetwork(self, network): + 'Used to track which network this organization is defined in.' + assert network in Network, 'Network not recognized ({0}), expected to be one of ({1})'.format(network, list(Network)) + if not network in self.networks: + self.networks.append(network) + + +class Directory: + + def __init__(self): + self.organizations = {} + self.users = {} + self.ordererAdminTuples = {} + + def registerOrg(self, orgName, network): + assert orgName not in self.organizations, "Organization already registered {0}".format(orgName) + self.organizations[orgName] = Organization(orgName) + return self.organizations[orgName] + + def registerUser(self, userName): + assert userName not in self.users, "User already registered {0}".format(userName) + self.users[userName] = User(userName) + return self.users[userName] + + def getUser(self, userName, shouldCreate = False): + if not userName in self.users and shouldCreate: + self.users[userName] = User(userName) + return self.users[userName] + + def getOrganization(self, orgName, shouldCreate = False): + if not orgName in self.organizations and shouldCreate: + self.organizations[orgName] = Organization(orgName) + return self.organizations[orgName] + + def registerOrdererAdminTuple(self, userName, ordererName, organizationName): + ' Assign the user as orderer admin' + ordererAdminTuple = NodeAdminTuple(user = userName, nodeName = ordererName, organization = organizationName) + assert ordererAdminTuple not in self.ordererAdminTuples, "Orderer admin tuple already registered {0}".format(ordererAdminTuple) + assert organizationName in self.organizations, "Orderer Organization not defined {0}".format(organizationName) + + user = self.getUser(userName, shouldCreate = True) + certReq = user.createCertRequest(ordererAdminTuple.nodeName) + userCert = self.getOrganization(organizationName).createCertificate(certReq) + + # Verify the newly created certificate + store = crypto.X509Store() + # Assuming a list of trusted certs + for trustedCert in [self.getOrganization(organizationName).signedCert]: + store.add_cert(trustedCert) + # Create a certificate context using the store and the certificate to verify + store_ctx = crypto.X509StoreContext(store, userCert) + # Verify the certificate, returns None if it can validate the certificate + store_ctx.verify_certificate() + + print("new Certificate for '{0}' on orderer '{1}' signed by '{2}': \n{3}".format(userName, ordererName, organizationName, crypto.dump_certificate(crypto.FILETYPE_PEM, userCert))) + self.ordererAdminTuples[ordererAdminTuple] = userCert + + +class AuthDSLHelper: + + @classmethod + def Envelope(cls, signaturePolicy, identities): + 'Envelope builds an envelope message embedding a SignaturePolicy' + return common_dot_configuration_pb2.SignaturePolicyEnvelope( + Version=0, + Policy=signaturePolicy, + Identities=identities) + + @classmethod + def NOutOf(cls, n, policies): + 'NOutOf creates a policy which requires N out of the slice of policies to evaluate to true' + return common_dot_configuration_pb2.SignaturePolicy( + From=common_dot_configuration_pb2.SignaturePolicy.NOutOf( + N=n, + Policies=policies, + ), + ) + + + +class BootstrapHelper: + KEY_CONSENSUS_TYPE = "ConsensusType" + KEY_CHAIN_CREATORS = "ChainCreators" + KEY_ACCEPT_ALL_POLICY = "AcceptAllPolicy" + KEY_BATCH_SIZE = "BatchSize" + + DEFAULT_MODIFICATION_POLICY_ID = "DefaultModificationPolicy" + DEFAULT_CHAIN_CREATORS = [KEY_ACCEPT_ALL_POLICY] + + DEFAULT_NONCE_SIZE = 24 + + def __init__(self, chainId = "TestChain", lastModified = 0, msgVersion = 1, epoch = 0, consensusType = "solo", batchSize = 10): + self.chainId = str(chainId) + self.lastModified = lastModified + self.msgVersion = msgVersion + self.epoch = epoch + self.consensusType = consensusType + self.batchSize = batchSize + + def getNonce(self): + return rand.bytes(self.DEFAULT_NONCE_SIZE) + + def makeChainHeader(self, type = common_dot_common_pb2.HeaderType.Value("CONFIGURATION_ITEM"), version = 1, + timestamp = timestamp_pb2.Timestamp(seconds = int(time.time()), nanos = 0)): + return common_dot_common_pb2.ChainHeader(type = type, + version = version, + timestamp = timestamp, + chainID = self.chainId, + epoch = self.epoch) + def makeSignatureHeader(self, serializeCertChain, nonce): + return common_dot_common_pb2.ChainHeader(type = type, + version = version, + timestamp = timestamp, + chainID = self.chainId, + epoch = self.epoch) + + def signConfigItem(self, configItem): + signedConfigItem = common_dot_configuration_pb2.SignedConfigurationItem(ConfigurationItem=configItem.SerializeToString(), Signatures=None) + return signedConfigItem + + def getConfigItem(self, commonConfigType, key, value): + configItem = common_dot_configuration_pb2.ConfigurationItem( + Header=self.makeChainHeader(type=common_dot_common_pb2.HeaderType.Value("CONFIGURATION_ITEM")), + Type=commonConfigType, + LastModified=self.lastModified, + ModificationPolicy=BootstrapHelper.DEFAULT_MODIFICATION_POLICY_ID, + Key=key, + Value=value) + return configItem + + def encodeBatchSize(self): + configItem = self.getConfigItem( + commonConfigType=common_dot_configuration_pb2.ConfigurationItem.ConfigurationType.Value("Orderer"), + key=BootstrapHelper.KEY_BATCH_SIZE, + value=orderer_dot_configuration_pb2.BatchSize(messages=self.batchSize).SerializeToString()) + return self.signConfigItem(configItem) + + def encodeConsensusType(self): + configItem = self.getConfigItem( + commonConfigType=common_dot_configuration_pb2.ConfigurationItem.ConfigurationType.Value("Orderer"), + key=BootstrapHelper.KEY_CONSENSUS_TYPE, + value=orderer_dot_configuration_pb2.ConsensusType(type=self.consensusType).SerializeToString()) + return self.signConfigItem(configItem) + + def encodeChainCreators(self): + configItem = self.getConfigItem( + commonConfigType=common_dot_configuration_pb2.ConfigurationItem.ConfigurationType.Value("Orderer"), + key=BootstrapHelper.KEY_CHAIN_CREATORS, + value=orderer_dot_configuration_pb2.ChainCreators(policies=BootstrapHelper.DEFAULT_CHAIN_CREATORS).SerializeToString()) + return self.signConfigItem(configItem) + + + def _encodeSignaturePolicyEnvelope(self, signaturePolicyEnvelope): + configItem = self.getConfigItem( + commonConfigType=common_dot_configuration_pb2.ConfigurationItem.ConfigurationType.Value("Orderer"), + key=BootstrapHelper.DEFAULT_MODIFICATION_POLICY_ID, + value=signaturePolicyEnvelope.SerializeToString()) + return self.signConfigItem(configItem) + + def encodeAcceptAllPolicy(self): + acceptAllPolicy = AuthDSLHelper.Envelope(signaturePolicy=AuthDSLHelper.NOutOf(0,[]), identities=[]) + return self._encodeSignaturePolicyEnvelope(acceptAllPolicy) + + def lockDefaultModificationPolicy(self): + rejectAllPolicy = AuthDSLHelper.Envelope(signaturePolicy=AuthDSLHelper.NOutOf(1,[]), identities=[]) + return self._encodeSignaturePolicyEnvelope(rejectAllPolicy) + + def computeBlockDataHash(self, blockData): + return computeCryptoHash(blockData.SerializeToString()) + +# Registerses a user on a specific composeService +def getDirectory(context): + if 'bootstrapDirectory' not in context: + context.bootstrapDirectory = Directory() + return context.bootstrapDirectory + + +def createGenesisBlock(context, chainId, networkConfigPolicy, consensusType): + 'Generates the genesis block for starting the oderers and for use in the chain config transaction by peers' + #assert not "bootstrapGenesisBlock" in context,"Genesis block already created:\n{0}".format(context.bootstrapGenesisBlock) + directory = getDirectory(context) + assert len(directory.ordererAdminTuples) > 0, "No orderer admin tuples defined!!!" + + bootstrapHelper = BootstrapHelper(chainId = chainId, consensusType=consensusType) + configItems = [] + configItems.append(bootstrapHelper.encodeBatchSize()) + configItems.append(bootstrapHelper.encodeConsensusType()) + configItems.append(bootstrapHelper.encodeChainCreators()) + configItems.append(bootstrapHelper.encodeAcceptAllPolicy()) + configItems.append(bootstrapHelper.lockDefaultModificationPolicy()) + configEnvelope = common_dot_configuration_pb2.ConfigurationEnvelope(Items=configItems) + + + payloadChainHeader = bootstrapHelper.makeChainHeader(type=common_dot_common_pb2.HeaderType.Value("CONFIGURATION_TRANSACTION")) + + #Now the SignatureHeader + serializedCreatorCertChain = None + nonce = None + payloadSignatureHeader = common_dot_common_pb2.SignatureHeader( + creator=serializedCreatorCertChain, + nonce=bootstrapHelper.getNonce(), + ) + + payloadHeader = common_dot_common_pb2.Header( + chainHeader=payloadChainHeader, + signatureHeader=payloadSignatureHeader, + ) + payload = common_dot_common_pb2.Payload(header=payloadHeader, data=configEnvelope.SerializeToString()) + envelope = common_dot_common_pb2.Envelope(payload=payload.SerializeToString(), signature=None) + + blockData = common_dot_common_pb2.BlockData(Data=[envelope.SerializeToString()]) + + + block = common_dot_common_pb2.Block( + Header=common_dot_common_pb2.BlockHeader( + Number=0, + PreviousHash=None, + DataHash=bootstrapHelper.computeBlockDataHash(blockData), + ), + Data=blockData, + Metadata=None, + ) + + # Add this back once crypto certs are required + for nodeAdminTuple in directory.ordererAdminTuples: + userCert = directory.ordererAdminTuples[nodeAdminTuple] + certAsPEM = crypto.dump_certificate(crypto.FILETYPE_PEM, userCert) + # print("UserCert for orderer genesis:\n{0}\n".format(certAsPEM)) + # print("") + + return block + +class PathType(Enum): + 'Denotes whether Path relative to Local filesystem or Containers volume reference.' + Local = 1 + Container = 2 + + +class OrdererGensisBlockCompositionCallback(compose.CompositionCallback): + 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' + + def __init__(self, context, genesisBlock, genesisFileName = "genesis_file"): + self.context = context + self.genesisFileName = genesisFileName + self.genesisBlock = genesisBlock + 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/orderer/{1}".format(basePath, composition.projectName) + + def getGenesisFilePath(self, composition, pathType=PathType.Local): + return "{0}/{1}".format(self.getVolumePath(composition, pathType), self.genesisFileName) + + def composing(self, composition, context): + print("Will copy gensisiBlock over at this point ") + os.makedirs(self.getVolumePath(composition)) + with open(self.getGenesisFilePath(composition), "wb") as f: + f.write(self.genesisBlock.SerializeToString()) + + 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): + env["ORDERER_GENERAL_GENESISIFILE"]=self.getGenesisFilePath(composition, pathType=PathType.Container) + + + +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 41859a4ac33..784991d5bef 100644 --- a/bddtests/steps/compose.py +++ b/bddtests/steps/compose.py @@ -19,15 +19,52 @@ import peer_basic_impl import json +from abc import ABCMeta, abstractmethod + +class CompositionCallback: + __metaclass__ = ABCMeta + @abstractmethod + def composing(self, composition, context): + pass + @abstractmethod + def decomposing(self, composition, context): + pass + @abstractmethod + def getEnv(self, composition, context, env): + pass + +class Test(CompositionCallback): + def composing(self, composition, context): + pass + def decomposing(self, composition, context): + pass + def getEnv(self, composition, context, env): + pass + class Composition: + @classmethod + def RegisterCallbackInContext(cls, context, callback): + if not isinstance(callback, CompositionCallback): + raise TypeError("Expected type to be {0}, instead received {1}".format(CompositionCallback, type(callback))) + Composition.GetCompositionCallbacksFromContext(context).append(callback) + + @classmethod + def GetCompositionCallbacksFromContext(cls, context): + if not "compositionCallbacks" in context: + context.compositionCallbacks = [] + return context.compositionCallbacks + + def GetUUID(): return str(uuid.uuid1()).replace('-','') - def __init__(self, composeFilesYaml, projectName = GetUUID()): + def __init__(self, context, composeFilesYaml, projectName = GetUUID()): self.projectName = projectName + self.context = context self.containerDataList = [] self.composeFilesYaml = composeFilesYaml + [callback.composing(self, context) for callback in Composition.GetCompositionCallbacksFromContext(context)] self.issueCommand(["up", "--force-recreate", "-d"]) def parseComposeFilesArg(self, composeFileArgs): @@ -41,6 +78,8 @@ def getEnv(self): myEnv = os.environ.copy() myEnv["COMPOSE_PROJECT_NAME"] = self.projectName myEnv["CORE_PEER_NETWORKID"] = self.projectName + # Invoke callbacks + [callback.getEnv(self, self.context, myEnv) for callback in Composition.GetCompositionCallbacksFromContext(self.context)] return myEnv def refreshContainerIDs(self): @@ -87,10 +126,21 @@ def decompose(self): self.issueCommand(["unpause"]) self.issueCommand(["kill"]) self.issueCommand(["rm", "-f"]) + # Now remove associated chaincode containers if any - #c.dockerHelper.RemoveContainersWithNamePrefix(c.projectName) output, error, returncode = \ bdd_test_util.cli_call(["docker"] + ["ps", "-qa", "--filter", "name={0}".format(self.projectName)], expect_success=True, env=self.getEnv()) for containerId in output.splitlines(): output, error, returncode = \ bdd_test_util.cli_call(["docker"] + ["rm", "-f", containerId], expect_success=True, env=self.getEnv()) + + # Remove the associated network + output, error, returncode = \ + bdd_test_util.cli_call(["docker"] + ["network", "ls", "-q", "--filter", "name={0}".format(self.projectName)], expect_success=True, env=self.getEnv()) + for networkId in output.splitlines(): + output, error, returncode = \ + bdd_test_util.cli_call(["docker"] + ["network", "rm", networkId], expect_success=True, env=self.getEnv()) + + # Invoke callbacks + [callback.decomposing(self, self.context) for callback in Composition.GetCompositionCallbacksFromContext(self.context)] + diff --git a/bddtests/steps/peer_basic_impl.py b/bddtests/steps/peer_basic_impl.py index c287e1be16a..c6032a25b74 100644 --- a/bddtests/steps/peer_basic_impl.py +++ b/bddtests/steps/peer_basic_impl.py @@ -68,7 +68,7 @@ def getDockerComposeFileArgsFromYamlFile(compose_yaml): @given(u'we compose "{composeYamlFile}"') def step_impl(context, composeYamlFile): # time.sleep(10) # Should be replaced with a definitive interlock guaranteeing that all peers/membersrvc are ready - composition = compose.Composition(composeYamlFile) + composition = compose.Composition(context, composeYamlFile) context.compose_containers = composition.containerDataList context.composition = composition