From 6b636f930bc43dbc9b25d1e6f35b988b3e032bd7 Mon Sep 17 00:00:00 2001 From: yacovm Date: Tue, 14 Nov 2017 16:10:47 +0200 Subject: [PATCH] [FAB-6230] peer CLI support for cfg lifecycle This change set: - Extends LSCC.GETINSTALLEDCHAINCODES to return also the ID to be used for the config update Txn by extending the protos/peer/query.proto and adding an ID for a chaincode. - Adds a new ability to the peer CLI- to support instantiation\upgrade in the config lifecycle way. the flow now is: - The peer CLI queries CSCC to obtain the resource config. - If the a bad status (not OK) is returned it means the channel doesn't have the capability and it resumes the flow of the old instantiate. - If a good (OK) status is returned, it queries LSCC and searches for the chaincode's ID (hashes of code and metadata) - Assuming it is found, it uses the old config fetched from the peer, and the ID of the chaincode, and the CLI parameters to build the new config, and submits it to the orderer as a resource update Txn. - Afterwards, it continues the usual flow of instantiate and upgrade (invoking LSCC and init of the target chaincode) - It can also: - Instead of sending the config update, save it to disk to be passed for another person. This is via the --resourceEnvelopeSavePath flag (-S) - Loaded from disk for appending a signature and optionally afterwards - sending the new config transaction for ordering. this is via the --resourceEnvelopeLoadPath flag (-L) Change-Id: I0f28a4f55249b7cd60d6d06e21ddb5267721a802 Signed-off-by: yacovm --- peer/chaincode/chaincode.go | 44 ++++++----- peer/chaincode/instantiate.go | 50 +++++++----- peer/chaincode/instantiate_test.go | 19 ++++- peer/chaincode/invoke_test.go | 35 ++++----- peer/chaincode/resources.go | 103 ++++++++++++++++++++++++ peer/chaincode/resources_test.go | 122 +++++++++++++++++++++++++++++ peer/chaincode/upgrade.go | 52 +++++++----- peer/chaincode/upgrade_test.go | 63 +++++---------- 8 files changed, 366 insertions(+), 122 deletions(-) diff --git a/peer/chaincode/chaincode.go b/peer/chaincode/chaincode.go index 888ec3c0068..9d2bbfc2847 100644 --- a/peer/chaincode/chaincode.go +++ b/peer/chaincode/chaincode.go @@ -50,24 +50,26 @@ func Cmd(cf *ChaincodeCmdFactory) *cobra.Command { // Chaincode-related variables. var ( - chaincodeLang string - chaincodeCtorJSON string - chaincodePath string - chaincodeName string - chaincodeUsr string // Not used - chaincodeQueryRaw bool - chaincodeQueryHex bool - customIDGenAlg string - channelID string - chaincodeVersion string - policy string - escc string - vscc string - policyMarshalled []byte - orderingEndpoint string - tls bool - caFile string - transient string + chaincodeLang string + chaincodeCtorJSON string + chaincodePath string + chaincodeName string + chaincodeUsr string // Not used + chaincodeQueryRaw bool + chaincodeQueryHex bool + customIDGenAlg string + channelID string + chaincodeVersion string + policy string + escc string + vscc string + policyMarshalled []byte + orderingEndpoint string + tls bool + caFile string + transient string + resourceEnvelopeSavePath string + resourceEnvelopeLoadPath string ) var chaincodeCmd = &cobra.Command{ @@ -108,6 +110,10 @@ func resetFlags() { fmt.Sprint("The name of the endorsement system chaincode to be used for this chaincode")) flags.StringVarP(&vscc, "vscc", "V", common.UndefinedParamValue, fmt.Sprint("The name of the verification system chaincode to be used for this chaincode")) + flags.StringVarP(&resourceEnvelopeSavePath, "resourceEnvelopeSavePath", "S", common.UndefinedParamValue, + fmt.Sprint("Specifies the file to save the resource config update to. If not specified, sends config update")) + flags.StringVarP(&resourceEnvelopeLoadPath, "resourceEnvelopeLoadPath", "L", common.UndefinedParamValue, + fmt.Sprint("Specifies the file to load the resource config update from. If not specified, creates a new config update")) flags.BoolVarP(&getInstalledChaincodes, "installed", "", false, "Get the installed chaincodes on a peer") flags.BoolVarP(&getInstantiatedChaincodes, "instantiated", "", false, @@ -120,7 +126,7 @@ func attachFlags(cmd *cobra.Command, names []string) { if flag := flags.Lookup(name); flag != nil { cmdFlags.AddFlag(flag) } else { - logger.Fatalf("Could not find flag '%s' to attach to commond '%s'", name, cmd.Name()) + logger.Fatalf("Could not find flag '%s' to attach to command '%s'", name, cmd.Name()) } } } diff --git a/peer/chaincode/instantiate.go b/peer/chaincode/instantiate.go index e967a44ff2c..fd820ce3702 100644 --- a/peer/chaincode/instantiate.go +++ b/peer/chaincode/instantiate.go @@ -17,12 +17,12 @@ limitations under the License. package chaincode import ( - "errors" "fmt" - protcommon "github.com/hyperledger/fabric/protos/common" + "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" + "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/net/context" ) @@ -41,7 +41,17 @@ func instantiateCmd(cf *ChaincodeCmdFactory) *cobra.Command { Long: fmt.Sprint(instantiateDesc), ValidArgs: []string{"1"}, RunE: func(cmd *cobra.Command, args []string) error { - return chaincodeDeploy(cmd, args, cf) + cf1 := cf + if cf1 == nil { + var err error + cf1, err = InitCmdFactory(true, true) + if err != nil { + return err + } + } + return chaincodeDeploy(cf1, func() error { + return lsccInstantiate(cmd, cf1) + }) }, } flagList := []string{ @@ -53,14 +63,15 @@ func instantiateCmd(cf *ChaincodeCmdFactory) *cobra.Command { "policy", "escc", "vscc", + "resourceEnvelopeSavePath", + "resourceEnvelopeLoadPath", } attachFlags(chaincodeInstantiateCmd, flagList) - return chaincodeInstantiateCmd } //instantiate the command via Endorser -func instantiate(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, error) { +func instantiate(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*common.Envelope, error) { spec, err := getChaincodeSpec(cmd) if err != nil { return nil, err @@ -108,26 +119,29 @@ func instantiate(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envel // chaincodeDeploy instantiates the chaincode. On success, the chaincode name // (hash) is printed to STDOUT for use by subsequent chaincode-related CLI // commands. -func chaincodeDeploy(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error { +func chaincodeDeploy(cf *ChaincodeCmdFactory, sendInit sendInitTransaction) error { if channelID == "" { return errors.New("The required parameter 'channelID' is empty. Rerun the command with -C flag") } var err error - if cf == nil { - cf, err = InitCmdFactory(true, true) - if err != nil { - return err - } - } + defer cf.BroadcastClient.Close() - env, err := instantiate(cmd, cf) + + ss := &sigSupport{cf.Signer} + version, config, err := fetchResourceConfig(cf.EndorserClient, ss, channelID) if err != nil { - return err + return errors.Wrap(err, "failed probing channel version") } - - if env != nil { - err = cf.BroadcastClient.Send(env) + if version == v11 { + return configBasedLifecycleUpdate(ss, cf, config, sendInit) } + return sendInit() +} - return err +func lsccInstantiate(cmd *cobra.Command, cf *ChaincodeCmdFactory) error { + env, err := instantiate(cmd, cf) + if err != nil { + return err + } + return cf.BroadcastClient.Send(env) } diff --git a/peer/chaincode/instantiate_test.go b/peer/chaincode/instantiate_test.go index d4579c1a81e..7bd43542146 100644 --- a/peer/chaincode/instantiate_test.go +++ b/peer/chaincode/instantiate_test.go @@ -17,16 +17,27 @@ package chaincode import ( + "errors" "testing" + "github.com/hyperledger/fabric/peer/common" + "github.com/hyperledger/fabric/protos/peer" "github.com/stretchr/testify/assert" ) func TestInstantiateCmd(t *testing.T) { InitMSP() - mockCF, err := getMockChaincodeCmdFactory() - assert.NoError(t, err, "Error getting mock chaincode command factory") + newMockCF := func() *ChaincodeCmdFactory { + mockCF, err := getMockChaincodeCmdFactoryWithEnorserResponses(common.MockResponse{Error: errors.New("chaincode error")}, common.MockResponse{ + Response: &peer.ProposalResponse{ + Response: &peer.Response{Status: 200}, + Endorsement: &peer.Endorsement{}, + }, + }) + assert.NoError(t, err, "Error getting mock chaincode command factory") + return mockCF + } // basic function tests var tests = []struct { @@ -75,10 +86,10 @@ func TestInstantiateCmd(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { resetFlags() - cmd := instantiateCmd(mockCF) + cmd := instantiateCmd(newMockCF()) addFlags(cmd) cmd.SetArgs(test.args) - err = cmd.Execute() + err := cmd.Execute() checkError(t, err, test.errorExpected, test.errMsg) }) } diff --git a/peer/chaincode/invoke_test.go b/peer/chaincode/invoke_test.go index 6ac5eed9513..c6d770c7e6c 100644 --- a/peer/chaincode/invoke_test.go +++ b/peer/chaincode/invoke_test.go @@ -179,38 +179,31 @@ func TestInvokeCmdEndorsementFailure(t *testing.T) { // Returns mock chaincode command factory func getMockChaincodeCmdFactory() (*ChaincodeCmdFactory, error) { - signer, err := common.GetDefaultSigner() - if err != nil { - return nil, err - } - mockResponse := &pb.ProposalResponse{ - Response: &pb.Response{Status: 200}, - Endorsement: &pb.Endorsement{}, - } - mockEndorserClient := common.GetMockEndorserClient(mockResponse, nil) - mockBroadcastClient := common.GetMockBroadcastClient(nil) - mockCF := &ChaincodeCmdFactory{ - EndorserClient: mockEndorserClient, - Signer: signer, - BroadcastClient: mockBroadcastClient, - } - return mockCF, nil + return getMockChaincodeCmdFactoryWithEnorserResponses(common.MockResponse{ + Response: &pb.ProposalResponse{ + Response: &pb.Response{Status: 200}, + Endorsement: &pb.Endorsement{}, + }, + }) } // Returns mock chaincode command factory that is constructed with an endorser // client that returns an error for proposal request func getMockChaincodeCmdFactoryWithErr() (*ChaincodeCmdFactory, error) { + return getMockChaincodeCmdFactoryWithEnorserResponses(common.MockResponse{ + Error: errors.New("invoke error"), + }) +} + +func getMockChaincodeCmdFactoryWithEnorserResponses(responses ...common.MockResponse) (*ChaincodeCmdFactory, error) { signer, err := common.GetDefaultSigner() if err != nil { return nil, err } - - errMsg := "invoke error" - mockEndorerClient := common.GetMockEndorserClient(nil, errors.New(errMsg)) + mockEndorserClient := common.GetMockMultiEndorserClient(responses...) mockBroadcastClient := common.GetMockBroadcastClient(nil) - mockCF := &ChaincodeCmdFactory{ - EndorserClient: mockEndorerClient, + EndorserClient: mockEndorserClient, Signer: signer, BroadcastClient: mockBroadcastClient, } diff --git a/peer/chaincode/resources.go b/peer/chaincode/resources.go index a5566b5caf6..cdbc062600b 100644 --- a/peer/chaincode/resources.go +++ b/peer/chaincode/resources.go @@ -12,11 +12,17 @@ import ( "strings" "time" + "io/ioutil" + "os" + "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/common/resourcesconfig" + update2 "github.com/hyperledger/fabric/common/tools/configtxlator/update" "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/scc/lscc" + "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" @@ -325,3 +331,100 @@ func getModPolicies(ccGrp *common.ConfigGroup, update ccUpdate) map[string]strin } return modPolicies } + +func configBasedLifecycleUpdate(ss *sigSupport, cf *ChaincodeCmdFactory, config *common.Config, sendInit sendInitTransaction) error { + var env *common.Envelope + hash, err := fetchCCID(ss, cf.EndorserClient, chaincodeName, chaincodeVersion) + if err != nil { + return err + } + if policy == "" { + return errors.New("empty policy") + } + pol, err := cauthdsl.FromString(policy) + if err != nil { + return err + } + update := ccUpdate{ + policy: pol, + computeDelta: update2.Compute, + ccName: chaincodeName, + oldConfig: config, + version: chaincodeVersion, + endorsement: escc, + validation: vscc, + hash: hash, + chainID: channelID, + SignatureSupport: &sigSupport{cf.Signer}, + } + if resourceEnvelopeLoadPath == "" { + // We're making a new config update + env = update.buildCCUpdateEnvelope() + } else { + // We're loading the config update from disk + updateEnv, err := loadEnvelope(resourceEnvelopeLoadPath) + if err != nil { + return err + } + // and appending our signature to it + updateEnv = update.appendSignature(updateEnv) + // and putting it back into an envelope + env = update.updateIntoEnvelope(updateEnv) + } + if resourceEnvelopeSavePath != "" { + return saveEnvelope(env) + } + if err := cf.BroadcastClient.Send(env); err != nil { + return err + } + return sendInit() +} + +func loadEnvelope(file string) (*common.ConfigUpdateEnvelope, error) { + data, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + env := &common.Envelope{} + if err := proto.Unmarshal(data, env); err != nil { + return nil, err + } + payload := &common.Payload{} + if err := proto.Unmarshal(env.Payload, payload); err != nil { + return nil, err + } + update := &common.ConfigUpdateEnvelope{} + if err := proto.Unmarshal(payload.Data, update); err != nil { + return nil, err + } + return update, nil +} + +func saveEnvelope(env *common.Envelope) error { + f, err := os.Create(resourceEnvelopeSavePath) + if err != nil { + return errors.Errorf("failed saving resource envelope to file %s: %v", resourceEnvelopeSavePath, err) + } + if _, err := f.Write(utils.MarshalOrPanic(env)); err != nil { + return errors.Errorf("failed saving resource envelope to file %s: %v", resourceEnvelopeSavePath, err) + } + fmt.Printf(`Saved config update envelope to %s. + You can now either: + 1) Append your signature using --resourceEnvelopeSave along with --resourceEnvelopeLoad + 2) Submit a transaction using only --resourceEnvelopeLoad + `, f.Name()) + return nil +} + +type sigSupport struct { + msp.SigningIdentity +} + +// NewSignatureHeader creates a new signature header +func (s *sigSupport) NewSignatureHeader() (*common.SignatureHeader, error) { + sID, err := s.Serialize() + if err != nil { + return nil, err + } + return utils.MakeSignatureHeader(sID, utils.CreateNonceOrPanic()), nil +} diff --git a/peer/chaincode/resources_test.go b/peer/chaincode/resources_test.go index c8a6c1f00e7..fd1f470e0d1 100644 --- a/peer/chaincode/resources_test.go +++ b/peer/chaincode/resources_test.go @@ -9,6 +9,7 @@ package chaincode import ( "errors" "fmt" + "os" "testing" "time" @@ -260,6 +261,127 @@ func (cube configUpdateBroadcastEvent) signatureCount() int { return len(update.Signatures) } +func TestDeployResource(t *testing.T) { + policy = "OR ('Org1MSP.member','Org2MSP.member')" + defer func() { + policy = "" + }() + pol := &common.SignaturePolicyEnvelope{} + channelID = "mychannel" + noopInit := func() error { + return nil + } + chaincodeName = "example02" + chaincodeVersion = "1.0" + config, _ := proto.Marshal(&peer.ConfigTree{ + ResourcesConfig: &common.Config{ + ChannelGroup: &common.ConfigGroup{ + Groups: map[string]*common.ConfigGroup{ + resourcesconfig.ChaincodesGroupKey: { + Groups: map[string]*common.ConfigGroup{ + "example02": ccGroup("example02", "1.0", "vscc", "escc", []byte("hash"), nil, pol), + }, + }, + }, + }, + }, + }) + chaincodes, _ := proto.Marshal(&peer.ChaincodeQueryResponse{ + Chaincodes: []*peer.ChaincodeInfo{ + { + Id: []byte("hash"), + Version: "1.0", + Name: "example02", + }, + }, + }) + + newTestCase := func(creator []byte) (*ChaincodeCmdFactory, *configUpdateBroadcastEvent) { + snr := (&mockSigningIdentity{}). + thatSerializes(creator, 8).thatSigns([]byte{1, 2, 3}, 8). + thatCreatesSignatureHeader(&common.SignatureHeader{}) + ec := &mockEndorserClient{} + ec.On("ProcessProposal").Return(&peer.ProposalResponse{ + Response: &peer.Response{ + Status: shim.OK, + Payload: config, + }, + }, nil).Times(1) + ec.On("ProcessProposal").Return(&peer.ProposalResponse{ + Response: &peer.Response{ + Status: shim.OK, + Payload: chaincodes, + }, + }, nil).Times(1) + + ec.On("ProcessProposal").Return(&peer.ProposalResponse{ + Response: &peer.Response{ + Status: shim.ERROR, + }, + }, nil).Once() + + bc := common2.GetMockBroadcastClient(nil) + + return &ChaincodeCmdFactory{ + EndorserClient: ec, + Signer: snr, + BroadcastClient: bc, + }, &configUpdateBroadcastEvent{bc} + } + + // Happy paths: peer returns the config and the chaincodes properly + + // Config update is sent to ordering + creatorBytes := []byte{1, 2, 3} + cmd, broadcastEvent := newTestCase(creatorBytes) + err := chaincodeDeploy(cmd, noopInit) + assert.NoError(t, err) + assert.True(t, broadcastEvent.wasSent(), "Config update wasn't sent to ordering") + + // Config update is saved to disk + resourceEnvelopeSavePath = fmt.Sprintf("/tmp/%d.pb", os.Getpid()) + defer os.Remove(resourceEnvelopeSavePath) + cmd, broadcastEvent = newTestCase(creatorBytes) + err = chaincodeDeploy(cmd, noopInit) + assert.NoError(t, err) + assert.False(t, broadcastEvent.wasSent(), "Config update was sent to ordering, but shouldn't have") + + // Config update is loaded from disk and a signature is appended by another identity + resourceEnvelopeLoadPath = resourceEnvelopeSavePath + cmd, broadcastEvent = newTestCase(append(creatorBytes, 1)) + err = chaincodeDeploy(cmd, noopInit) + assert.NoError(t, err) + assert.False(t, broadcastEvent.wasSent(), "Config update was sent to ordering, but shouldn't have") + + // Config update is loaded from disk by the 1st identity, and sent for ordering + resourceEnvelopeSavePath = "" + cmd, broadcastEvent = newTestCase(creatorBytes) + err = chaincodeDeploy(cmd, noopInit) + assert.NoError(t, err) + assert.True(t, broadcastEvent.wasSent(), "Config update was not sent to ordering, but shouldn't have") + assert.Equal(t, 2, broadcastEvent.signatureCount(), "Expected 2 signatures in config update") + + // Bad path: peer returns the config but the chaincodes it returns don't have the wanted name + chaincodeName = "example03" + cmd, broadcastEvent = newTestCase(creatorBytes) + err = chaincodeDeploy(cmd, noopInit) + assert.Error(t, err) + assert.Equal(t, "chaincode with name example03 and version 1.0 wasn't found", err.Error()) + + // Bad path: peer returns an error when it is probed + ec := &mockEndorserClient{} + snr := (&mockSigningIdentity{}).thatSerializes([]byte{1, 2, 3}, 3).thatSigns([]byte{1, 2, 3}, 3) + ec.On("ProcessProposal").Return(nil, errors.New("endorsement failed")) + err = chaincodeDeploy(&ChaincodeCmdFactory{ + EndorserClient: ec, + Signer: snr, + BroadcastClient: common2.GetMockBroadcastClient(nil), + }, noopInit) + assert.Error(t, err) + assert.Equal(t, "failed probing channel version: endorsement failed", err.Error()) + +} + type mockSigningIdentity struct { mock.Mock } diff --git a/peer/chaincode/upgrade.go b/peer/chaincode/upgrade.go index 6e56c81f29a..a16ad2b0c67 100644 --- a/peer/chaincode/upgrade.go +++ b/peer/chaincode/upgrade.go @@ -17,12 +17,12 @@ limitations under the License. package chaincode import ( - "errors" "fmt" protcommon "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric/protos/utils" + "github.com/pkg/errors" "github.com/spf13/cobra" "golang.org/x/net/context" ) @@ -39,7 +39,27 @@ func upgradeCmd(cf *ChaincodeCmdFactory) *cobra.Command { Long: "Upgrade an existing chaincode with the specified one. The new chaincode will immediately replace the existing chaincode upon the transaction committed.", ValidArgs: []string{"1"}, RunE: func(cmd *cobra.Command, args []string) error { - return chaincodeUpgrade(cmd, args, cf) + cf1 := cf + return chaincodeUpgrade(cf1, func() error { + var err error + if cf1 == nil { + cf1, err = InitCmdFactory(true, true) + if err != nil { + return err + } + } + env, err := upgrade(cmd, cf1) + if err != nil { + return err + } + + if env != nil { + logger.Debug("Send signed envelope to orderer") + err = cf1.BroadcastClient.Send(env) + return err + } + return nil + }) }, } flagList := []string{ @@ -52,6 +72,8 @@ func upgradeCmd(cf *ChaincodeCmdFactory) *cobra.Command { "policy", "escc", "vscc", + "resourceEnvelopeSavePath", + "resourceEnvelopeLoadPath", } attachFlags(chaincodeUpgradeCmd, flagList) @@ -111,26 +133,20 @@ func upgrade(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, // chaincodeUpgrade upgrades the chaincode. On success, the new chaincode // version is printed to STDOUT -func chaincodeUpgrade(cmd *cobra.Command, args []string, cf *ChaincodeCmdFactory) error { - var err error - if cf == nil { - cf, err = InitCmdFactory(true, true) - if err != nil { - return err - } +func chaincodeUpgrade(cf *ChaincodeCmdFactory, sendInit sendInitTransaction) error { + if channelID == "" { + return errors.New("The required parameter 'channelID' is empty. Rerun the command with -C flag") } + var err error defer cf.BroadcastClient.Close() - env, err := upgrade(cmd, cf) + ss := &sigSupport{cf.Signer} + version, config, err := fetchResourceConfig(cf.EndorserClient, ss, channelID) if err != nil { - return err + return errors.Wrap(err, "failed probing channel version") } - - if env != nil { - logger.Debug("Send signed envelope to orderer") - err = cf.BroadcastClient.Send(env) - return err + if version == v11 { + return configBasedLifecycleUpdate(ss, cf, config, sendInit) } - - return nil + return sendInit() } diff --git a/peer/chaincode/upgrade_test.go b/peer/chaincode/upgrade_test.go index 00423dfca89..e86ebb72208 100644 --- a/peer/chaincode/upgrade_test.go +++ b/peer/chaincode/upgrade_test.go @@ -22,6 +22,8 @@ import ( "sync" "testing" + "strings" + "github.com/hyperledger/fabric/msp/mgmt/testtools" "github.com/hyperledger/fabric/peer/common" pb "github.com/hyperledger/fabric/protos/peer" @@ -43,39 +45,29 @@ func initMSP() { } func TestUpgradeCmd(t *testing.T) { + channelID = "" InitMSP() - signer, err := common.GetDefaultSigner() - if err != nil { - t.Fatalf("Get default signer error: %v", err) - } - - mockResponse := &pb.ProposalResponse{ - Response: &pb.Response{Status: 200}, - Endorsement: &pb.Endorsement{}, + newMockCF := func() *ChaincodeCmdFactory { + mockCF, err := getMockChaincodeCmdFactoryWithEnorserResponses(common.MockResponse{Error: fmt.Errorf("chaincode problem")}, common.MockResponse{ + Response: &pb.ProposalResponse{ + Response: &pb.Response{Status: 200}, + Endorsement: &pb.Endorsement{}, + }, + }) + assert.NoError(t, err, "Error getting mock chaincode command factory") + return mockCF } - mockEndorerClient := common.GetMockEndorserClient(mockResponse, nil) - - mockBroadcastClient := common.GetMockBroadcastClient(nil) - - mockCF := &ChaincodeCmdFactory{ - EndorserClient: mockEndorerClient, - Signer: signer, - BroadcastClient: mockBroadcastClient, - } // reset channelID, it might have been set by previous test - channelID = "" - - cmd := upgradeCmd(mockCF) + cmd := upgradeCmd(newMockCF()) addFlags(cmd) args := []string{"-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "anotherversion", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"} cmd.SetArgs(args) - err = cmd.Execute() + err := cmd.Execute() assert.Error(t, err, "'peer chaincode upgrade' command should have failed without -C flag") - args = []string{"-C", "mychannel", "-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "anotherversion", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"} cmd.SetArgs(args) @@ -125,28 +117,15 @@ func TestUpgradeCmdEndorseFail(t *testing.T) { func TestUpgradeCmdSendTXFail(t *testing.T) { InitMSP() - signer, err := common.GetDefaultSigner() - if err != nil { - t.Fatalf("Get default signer error: %v", err) - } - - mockResponse := &pb.ProposalResponse{ - Response: &pb.Response{Status: 200}, - Endorsement: &pb.Endorsement{}, - } - - mockEndorerClient := common.GetMockEndorserClient(mockResponse, nil) - sendErr := errors.New("send tx failed") - mockBroadcastClient := common.GetMockBroadcastClient(sendErr) - - mockCF := &ChaincodeCmdFactory{ - EndorserClient: mockEndorerClient, - Signer: signer, - BroadcastClient: mockBroadcastClient, + newMockCF := func() *ChaincodeCmdFactory { + mockCF, err := getMockChaincodeCmdFactoryWithEnorserResponses(common.MockResponse{Error: fmt.Errorf("chaincode problem")}, + common.MockResponse{Error: sendErr}) + assert.NoError(t, err, "Error getting mock chaincode command factory") + return mockCF } - cmd := upgradeCmd(mockCF) + cmd := upgradeCmd(newMockCF()) addFlags(cmd) args := []string{"-C", "mychannel", "-n", "example02", "-p", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", "-v", "anotherversion", "-c", "{\"Function\":\"init\",\"Args\": [\"param\",\"1\"]}"} @@ -156,7 +135,7 @@ func TestUpgradeCmdSendTXFail(t *testing.T) { if err := cmd.Execute(); err == nil { t.Errorf("Run chaincode upgrade cmd error:%v", err) } else { - if err.Error() != expectErrMsg { + if !strings.Contains(err.Error(), expectErrMsg) { t.Errorf("Run chaincode upgrade cmd get unexpected error: %s", err.Error()) } }