go_study/fabric-main/integration/lifecycle/lifecycle_test.go

408 lines
16 KiB
Go

/*
Copyright IBM Corp All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package lifecycle
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"syscall"
docker "github.com/fsouza/go-dockerclient"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-config/protolator"
"github.com/hyperledger/fabric-config/protolator/protoext/ordererext"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/integration/channelparticipation"
"github.com/hyperledger/fabric/integration/nwo"
"github.com/hyperledger/fabric/integration/nwo/commands"
"github.com/hyperledger/fabric/integration/nwo/fabricconfig"
"github.com/hyperledger/fabric/integration/nwo/runner"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
. "github.com/onsi/gomega/gstruct"
"github.com/tedsuo/ifrit"
ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2"
)
var _ = Describe("Lifecycle", func() {
var (
client *docker.Client
testDir string
network *nwo.Network
processes = map[string]ifrit.Process{}
ordererRunner *ginkgomon.Runner
ordererProcess, peerProcess ifrit.Process
termFiles []string
)
BeforeEach(func() {
var err error
testDir, err = ioutil.TempDir("", "lifecycle")
Expect(err).NotTo(HaveOccurred())
client, err = docker.NewClientFromEnv()
Expect(err).NotTo(HaveOccurred())
network = nwo.New(nwo.BasicEtcdRaft(), testDir, client, StartPort(), components)
// Generate config
network.GenerateConfigTree()
// configure only one of four peers (Org1, peer0) to use couchdb.
// Note that we do not support a channel with mixed DBs.
// However, for testing, it would be fine to use couchdb for one
// peer. We're using couchdb here to ensure all supported character
// classes in chaincode names/versions work on the supported db types.
couchDB := &runner.CouchDB{}
couchProcess := ifrit.Invoke(couchDB)
Eventually(couchProcess.Ready(), runner.DefaultStartTimeout).Should(BeClosed())
Consistently(couchProcess.Wait()).ShouldNot(Receive())
couchAddr := couchDB.Address()
peer := network.Peer("Org1", "peer0")
core := network.ReadPeerConfig(peer)
core.Ledger.State.StateDatabase = "CouchDB"
core.Ledger.State.CouchDBConfig.CouchDBAddress = couchAddr
processes[couchDB.Name] = couchProcess
setTermFileEnvForBinaryExternalBuilder := func(envKey, termFile string, externalBuilders []fabricconfig.ExternalBuilder) {
os.Setenv(envKey, termFile)
for i, e := range externalBuilders {
if e.Name == "binary" {
e.PropagateEnvironment = append(e.PropagateEnvironment, envKey)
externalBuilders[i] = e
}
}
}
org1TermFile := filepath.Join(testDir, "org1-term-file")
setTermFileEnvForBinaryExternalBuilder("ORG1_TERM_FILE", org1TermFile, core.Chaincode.ExternalBuilders)
network.WritePeerConfig(peer, core)
peer = network.Peer("Org2", "peer0")
core = network.ReadPeerConfig(peer)
org2TermFile := filepath.Join(testDir, "org2-term-file")
setTermFileEnvForBinaryExternalBuilder("ORG2_TERM_FILE", org2TermFile, core.Chaincode.ExternalBuilders)
network.WritePeerConfig(peer, core)
termFiles = []string{org1TermFile, org2TermFile}
// bootstrap the network
network.Bootstrap()
ordererRunner, ordererProcess, peerProcess = network.StartSingleOrdererNetwork("orderer")
})
AfterEach(func() {
// Shutdown processes and cleanup
if ordererProcess != nil {
ordererProcess.Signal(syscall.SIGTERM)
Eventually(ordererProcess.Wait(), network.EventuallyTimeout).Should(Receive())
}
if peerProcess != nil {
peerProcess.Signal(syscall.SIGTERM)
Eventually(peerProcess.Wait(), network.EventuallyTimeout).Should(Receive())
}
for _, p := range processes {
p.Signal(syscall.SIGTERM)
Eventually(p.Wait(), network.EventuallyTimeout).Should(Receive())
}
network.Cleanup()
os.RemoveAll(testDir)
})
It("deploys and executes chaincode using _lifecycle and upgrades it", func() {
orderer := network.Orderer("orderer")
testPeers := network.PeersWithChannel("testchannel")
org1peer0 := network.Peer("Org1", "peer0")
chaincodePath := components.Build("github.com/hyperledger/fabric/integration/chaincode/simple/cmd")
chaincode := nwo.Chaincode{
Name: "My_1st-Chaincode",
Version: "Version-0.0",
Path: chaincodePath,
Lang: "binary",
PackageFile: filepath.Join(testDir, "simplecc.tar.gz"),
Ctor: `{"Args":["init","a","100","b","200"]}`,
ChannelConfigPolicy: "/Channel/Application/Endorsement",
Sequence: "1",
InitRequired: true,
Label: "my_simple_chaincode",
}
By("setting up the channel")
channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner)
network.VerifyMembership(network.PeersWithChannel("testchannel"), "testchannel")
nwo.EnableCapabilities(network, "testchannel", "Application", "V2_0", orderer, network.Peer("Org1", "peer0"), network.Peer("Org2", "peer0"))
By("deploying the chaincode")
nwo.PackageChaincodeBinary(chaincode)
chaincode.SetPackageIDFromPackageFile()
nwo.InstallChaincode(network, chaincode, testPeers...)
By("verifying the installed chaincode package matches the one that was submitted")
sess, err := network.PeerAdminSession(testPeers[0], commands.ChaincodeGetInstalledPackage{
PackageID: chaincode.PackageID,
OutputDirectory: testDir,
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
fileBytes, err := ioutil.ReadFile(chaincode.PackageFile)
Expect(err).NotTo(HaveOccurred())
fileBytesFromPeer, err := ioutil.ReadFile(filepath.Join(network.RootDir, chaincode.PackageID+".tar.gz"))
Expect(err).NotTo(HaveOccurred())
Expect(fileBytesFromPeer).To(Equal(fileBytes))
nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, testPeers...)
nwo.CheckCommitReadinessUntilReady(network, "testchannel", chaincode, network.PeerOrgs(), testPeers...)
nwo.CommitChaincode(network, "testchannel", orderer, chaincode, testPeers[0], testPeers...)
nwo.InitChaincode(network, "testchannel", orderer, chaincode, testPeers...)
By("ensuring the chaincode can be invoked and queried")
endorsers := []*nwo.Peer{
network.Peer("Org1", "peer0"),
network.Peer("Org2", "peer0"),
}
RunQueryInvokeQuery(network, orderer, "My_1st-Chaincode", 100, endorsers...)
By("setting a bad package ID to temporarily disable endorsements on org1")
savedPackageID := chaincode.PackageID
// note that in theory it should be sufficient to set it to an
// empty string, but the ApproveChaincodeForMyOrg
// function fills the packageID field if empty
chaincode.PackageID = "bad"
nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, org1peer0)
By("querying the chaincode and expecting the invocation to fail")
sess, err = network.PeerUserSession(org1peer0, "User1", commands.ChaincodeQuery{
ChannelID: "testchannel",
Name: "My_1st-Chaincode",
Ctor: `{"Args":["query","a"]}`,
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
Expect(sess.Err).To(gbytes.Say("Error: endorsement failure during query. response: status:500 " +
"message:\"make sure the chaincode My_1st-Chaincode has been successfully defined on channel testchannel and try " +
"again: chaincode definition for 'My_1st-Chaincode' exists, but chaincode is not installed\""))
By("setting the correct package ID to restore the chaincode")
chaincode.PackageID = savedPackageID
nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, org1peer0)
By("querying the chaincode and expecting the invocation to succeed")
sess, err = network.PeerUserSession(org1peer0, "User1", commands.ChaincodeQuery{
ChannelID: "testchannel",
Name: "My_1st-Chaincode",
Ctor: `{"Args":["query","a"]}`,
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
Expect(sess).To(gbytes.Say("90"))
By("upgrading the chaincode to sequence 2 using a new chaincode package")
previousLabel := chaincode.Label
previousPackageID := chaincode.PackageID
chaincode.Label = "my_simple_chaincode_updated"
chaincode.PackageFile = filepath.Join(testDir, "simplecc-updated.tar.gz")
chaincode.Sequence = "2"
nwo.PackageChaincodeBinary(chaincode)
chaincode.SetPackageIDFromPackageFile()
nwo.InstallChaincode(network, chaincode, testPeers...)
nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, testPeers...)
nwo.CheckCommitReadinessUntilReady(network, "testchannel", chaincode, network.PeerOrgs(), testPeers...)
nwo.CommitChaincode(network, "testchannel", orderer, chaincode, testPeers[0], testPeers...)
By("listing the installed chaincodes and verifying the channel/chaincode definitions that are using the chaincode package")
nwo.QueryInstalledReferences(network, "testchannel", chaincode.Label, chaincode.PackageID, network.Peer("Org2", "peer0"), []string{"My_1st-Chaincode", "Version-0.0"})
By("checking for term files that are written when the chaincode is stopped via SIGTERM")
for _, termFile := range termFiles {
Expect(termFile).To(BeARegularFile())
}
By("ensuring the previous chaincode package is no longer referenced by a chaincode definition on the channel")
Expect(nwo.QueryInstalled(network, network.Peer("Org2", "peer0"))()).To(
ContainElement(MatchFields(IgnoreExtras,
Fields{
"Label": Equal(previousLabel),
"PackageId": Equal(previousPackageID),
"References": Not(HaveKey("testchannel")),
},
)),
)
By("ensuring the chaincode can still be invoked and queried")
RunQueryInvokeQuery(network, orderer, "My_1st-Chaincode", 90, endorsers...)
By("deploying another chaincode using the same chaincode package")
anotherChaincode := nwo.Chaincode{
Name: "Your_Chaincode",
Version: "Version+0_0",
Path: chaincodePath,
Lang: "binary",
PackageFile: filepath.Join(testDir, "simplecc.tar.gz"),
Ctor: `{"Args":["init","a","100","b","200"]}`,
ChannelConfigPolicy: "/Channel/Application/Endorsement",
Sequence: "1",
InitRequired: true,
Label: "my_simple_chaincode",
}
nwo.DeployChaincode(network, "testchannel", orderer, anotherChaincode)
By("listing the installed chaincodes and verifying the channel/chaincode definitions that are using the chaincode package")
anotherChaincode.SetPackageIDFromPackageFile()
nwo.QueryInstalledReferences(network, "testchannel", anotherChaincode.Label, anotherChaincode.PackageID, network.Peer("Org2", "peer0"), []string{"Your_Chaincode", "Version+0_0"})
By("adding a new org")
org3 := &nwo.Organization{
MSPID: "Org3MSP",
Name: "Org3",
Domain: "org3.example.com",
EnableNodeOUs: true,
Users: 2,
CA: &nwo.CA{
Hostname: "ca",
},
}
org3peer0 := &nwo.Peer{
Name: "peer0",
Organization: "Org3",
Channels: testPeers[0].Channels,
}
network.AddOrg(org3, org3peer0)
GenerateOrgUpdateMaterials(network, org3peer0)
By("starting the org3 peer")
pr := network.PeerRunner(org3peer0)
org3Process := ifrit.Invoke(pr)
processes[org3peer0.ID()] = org3Process
Eventually(org3Process.Ready(), network.EventuallyTimeout).Should(BeClosed())
By("updating the channel config to include org3")
// get the current channel config
currentConfig := nwo.GetConfig(network, testPeers[0], orderer, "testchannel")
updatedConfig := proto.Clone(currentConfig).(*common.Config)
// get the configtx info for org3
sess, err = network.ConfigTxGen(commands.PrintOrg{
ConfigPath: network.RootDir,
PrintOrg: "Org3",
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
org3Group := &ordererext.DynamicOrdererOrgGroup{ConfigGroup: &common.ConfigGroup{}}
err = protolator.DeepUnmarshalJSON(bytes.NewBuffer(sess.Out.Contents()), org3Group)
Expect(err).NotTo(HaveOccurred())
// update the channel config to include org3
updatedConfig.ChannelGroup.Groups["Application"].Groups["Org3"] = org3Group.ConfigGroup
nwo.UpdateConfig(network, orderer, "testchannel", currentConfig, updatedConfig, true, testPeers[0], testPeers...)
By("joining the org3 peers to the channel")
network.JoinChannel("testchannel", orderer, org3peer0)
// update testPeers now that org3 has joined
testPeers = network.PeersWithChannel("testchannel")
// wait until all peers, particularly those in org3, have received the block
// containing the updated config
maxLedgerHeight := nwo.GetMaxLedgerHeight(network, "testchannel", testPeers...)
nwo.WaitUntilEqualLedgerHeight(network, "testchannel", maxLedgerHeight, testPeers...)
By("querying definitions by org3 before performing any chaincode actions")
sess, err = network.PeerAdminSession(network.Peer("Org2", "peer0"), commands.ChaincodeListCommitted{
ChannelID: "testchannel",
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
By("installing the chaincode to the org3 peers")
nwo.InstallChaincode(network, chaincode, org3peer0)
By("ensuring org3 peers do not execute the chaincode before approving the definition")
org3AndOrg1PeerAddresses := []string{
network.PeerAddress(org3peer0, nwo.ListenPort),
network.PeerAddress(org1peer0, nwo.ListenPort),
}
sess, err = network.PeerUserSession(org3peer0, "User1", commands.ChaincodeInvoke{
ChannelID: "testchannel",
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
Name: "My_1st-Chaincode",
Ctor: `{"Args":["invoke","a","b","10"]}`,
PeerAddresses: org3AndOrg1PeerAddresses,
WaitForEvent: true,
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
Expect(sess.Err).To(gbytes.Say("chaincode definition for 'My_1st-Chaincode' at sequence 2 on channel 'testchannel' has not yet been approved by this org"))
By("org3 approving the chaincode definition")
nwo.ApproveChaincodeForMyOrg(network, "testchannel", orderer, chaincode, network.PeersInOrg("Org3")...)
nwo.EnsureChaincodeCommitted(network, "testchannel", chaincode.Name, chaincode.Version, chaincode.Sequence, []*nwo.Organization{network.Organization("Org1"), network.Organization("Org2"), network.Organization("Org3")}, org3peer0)
By("ensuring chaincode can be invoked and queried by org3")
org3andOrg1Endorsers := []*nwo.Peer{
network.Peer("Org3", "peer0"),
network.Peer("Org1", "peer0"),
}
RunQueryInvokeQuery(network, orderer, "My_1st-Chaincode", 80, org3andOrg1Endorsers...)
By("deploying a chaincode without an endorsement policy specified")
chaincode = nwo.Chaincode{
Name: "defaultpolicycc",
Version: "0.0",
Path: chaincodePath,
Lang: "binary",
PackageFile: filepath.Join(testDir, "modulecc.tar.gz"),
Ctor: `{"Args":["init","a","100","b","200"]}`,
Sequence: "1",
InitRequired: true,
Label: "my_simple_chaincode",
}
nwo.DeployChaincode(network, "testchannel", orderer, chaincode)
By("attempting to invoke the chaincode without a majority")
sess, err = network.PeerUserSession(org3peer0, "User1", commands.ChaincodeInvoke{
ChannelID: "testchannel",
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
Name: "defaultpolicycc",
Ctor: `{"Args":["invoke","a","b","10"]}`,
WaitForEvent: true,
})
Expect(err).ToNot(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
Expect(sess.Err).To(gbytes.Say(`\QError: transaction invalidated with status (ENDORSEMENT_POLICY_FAILURE)\E`))
By("attempting to invoke the chaincode with a majority")
sess, err = network.PeerUserSession(org3peer0, "User1", commands.ChaincodeInvoke{
ChannelID: "testchannel",
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
Name: "defaultpolicycc",
Ctor: `{"Args":["invoke","a","b","10"]}`,
PeerAddresses: org3AndOrg1PeerAddresses,
WaitForEvent: true,
})
Expect(err).NotTo(HaveOccurred())
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(0))
Expect(sess.Err).To(gbytes.Say(`\Qcommitted with status (VALID)\E`))
Expect(sess.Err).To(gbytes.Say(`Chaincode invoke successful. result: status:200`))
})
})