336 lines
11 KiB
Go
336 lines
11 KiB
Go
/*
|
|
Copyright IBM Corp All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package devmode
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
|
|
docker "github.com/fsouza/go-dockerclient"
|
|
"github.com/hyperledger/fabric/integration/channelparticipation"
|
|
"github.com/hyperledger/fabric/integration/nwo"
|
|
"github.com/hyperledger/fabric/integration/nwo/commands"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
"github.com/onsi/gomega/gbytes"
|
|
"github.com/onsi/gomega/gexec"
|
|
"github.com/tedsuo/ifrit"
|
|
ginkgomon "github.com/tedsuo/ifrit/ginkgomon_v2"
|
|
)
|
|
|
|
var _ = Describe("Devmode", func() {
|
|
var (
|
|
testDir string
|
|
client *docker.Client
|
|
network *nwo.Network
|
|
ordererRunner *ginkgomon.Runner
|
|
ordererProcess, peerProcess ifrit.Process
|
|
chaincode nwo.Chaincode
|
|
legacyChaincode nwo.Chaincode
|
|
chaincodeRunner *ginkgomon.Runner
|
|
chaincodeProcess ifrit.Process
|
|
channelName string
|
|
)
|
|
|
|
BeforeEach(func() {
|
|
var err error
|
|
channelName = "testchannel"
|
|
testDir, err = ioutil.TempDir("", "devmode")
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
client, err = docker.NewClientFromEnv()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
network = nwo.New(devModeEtcdraft, testDir, client, StartPort(), components)
|
|
|
|
network.TLSEnabled = false
|
|
network.Peer("Org1", "peer0").DevMode = true
|
|
|
|
network.GenerateConfigTree()
|
|
network.Bootstrap()
|
|
|
|
// Start all the fabric processes
|
|
ordererRunner, ordererProcess, peerProcess = network.StartSingleOrdererNetwork("orderer")
|
|
})
|
|
|
|
AfterEach(func() {
|
|
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())
|
|
}
|
|
|
|
if chaincodeProcess != nil {
|
|
chaincodeProcess.Signal(syscall.SIGTERM)
|
|
}
|
|
|
|
if network != nil {
|
|
network.Cleanup()
|
|
}
|
|
|
|
os.RemoveAll(testDir)
|
|
})
|
|
|
|
It("executes chaincode in dev mode using legacy lifecycle", func() {
|
|
legacyChaincode = nwo.Chaincode{
|
|
Name: "mycc",
|
|
Version: "0.0",
|
|
Path: "github.com/hyperledger/fabric/integration/chaincode/simple/cmd",
|
|
Ctor: `{"Args":["init","a","100","b","200"]}`,
|
|
Policy: `OR ('Org1MSP.member')`,
|
|
}
|
|
|
|
org1peer0 := network.Peer("Org1", "peer0")
|
|
orderer := network.Orderer("orderer")
|
|
|
|
By("setting up the channel")
|
|
channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner)
|
|
|
|
By("building chaincode")
|
|
chaincodeExecutePath := components.Build(legacyChaincode.Path)
|
|
|
|
By("running the chaincode")
|
|
legacyChaincodeID := legacyChaincode.Name + ":" + legacyChaincode.Version
|
|
peerChaincodeAddress := network.PeerAddress(org1peer0, nwo.ChaincodePort)
|
|
envs := []string{
|
|
"CORE_PEER_TLS_ENABLED=false",
|
|
"CORE_CHAINCODE_ID_NAME=" + legacyChaincodeID,
|
|
"DEVMODE_ENABLED=true",
|
|
}
|
|
cmd := exec.Command(chaincodeExecutePath, "-peer.address", peerChaincodeAddress)
|
|
cmd.Env = append(cmd.Env, envs...)
|
|
chaincodeRunner = ginkgomon.New(ginkgomon.Config{
|
|
Name: "chaincode",
|
|
Command: cmd,
|
|
StartCheckTimeout: 15 * time.Second,
|
|
StartCheck: "starting up in devmode...",
|
|
})
|
|
chaincodeProcess = ifrit.Invoke(chaincodeRunner)
|
|
Eventually(chaincodeProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
|
|
By("installing the chaincode")
|
|
nwo.InstallChaincodeLegacy(network, legacyChaincode, org1peer0)
|
|
|
|
By("instantiating the chaincode")
|
|
nwo.InstantiateChaincodeLegacy(network, channelName, orderer, legacyChaincode, org1peer0, org1peer0)
|
|
|
|
By("querying and invoking the chaincode")
|
|
RunQueryInvokeQuery(network, orderer, org1peer0, channelName, 100)
|
|
Eventually(chaincodeRunner).Should(gbytes.Say("invoking in devmode"))
|
|
})
|
|
|
|
It("executes chaincode in dev mode", func() {
|
|
chaincode = nwo.Chaincode{
|
|
Name: "mycc",
|
|
Version: "0.0",
|
|
Path: components.Build("github.com/hyperledger/fabric/integration/chaincode/simple/cmd"),
|
|
Lang: "binary",
|
|
PackageFile: filepath.Join(testDir, "simplecc.tar.gz"),
|
|
Ctor: `{"Args":["init","a","100","b","200"]}`,
|
|
SignaturePolicy: `OR ('Org1MSP.member')`,
|
|
Sequence: "1",
|
|
InitRequired: true,
|
|
Label: "my_prebuilt_chaincode",
|
|
}
|
|
|
|
org1peer0 := network.Peer("Org1", "peer0")
|
|
orderer := network.Orderer("orderer")
|
|
|
|
By("setting up the channel")
|
|
channelparticipation.JoinOrdererJoinPeersAppChannel(network, "testchannel", orderer, ordererRunner)
|
|
|
|
By("enabling V2_0 application capabilities")
|
|
nwo.EnableCapabilities(network, channelName, "Application", "V2_0", orderer, org1peer0)
|
|
|
|
By("running the chaincode")
|
|
chaincodeID := chaincode.Name + ":" + chaincode.Version
|
|
peerChaincodeAddress := network.PeerAddress(org1peer0, nwo.ChaincodePort)
|
|
envs := []string{
|
|
"CORE_PEER_TLS_ENABLED=false",
|
|
"CORE_PEER_ADDRESS=" + peerChaincodeAddress,
|
|
"CORE_CHAINCODE_ID_NAME=" + chaincodeID,
|
|
"DEVMODE_ENABLED=true",
|
|
}
|
|
|
|
cmd := exec.Command(chaincode.Path, "-peer.address", peerChaincodeAddress)
|
|
cmd.Env = append(cmd.Env, envs...)
|
|
chaincodeRunner = ginkgomon.New(ginkgomon.Config{
|
|
Name: "chaincode",
|
|
Command: cmd,
|
|
StartCheckTimeout: 15 * time.Second,
|
|
StartCheck: "starting up in devmode...",
|
|
})
|
|
chaincodeProcess = ifrit.Invoke(chaincodeRunner)
|
|
Eventually(chaincodeProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
|
|
By("approving chaincode for orgs")
|
|
ApproveChaincodeForMyOrg(network, channelName, orderer, chaincode, org1peer0)
|
|
By("committing the chaincode definition")
|
|
nwo.CheckCommitReadinessUntilReady(network, channelName, chaincode, network.PeerOrgs(), org1peer0)
|
|
nwo.CommitChaincode(network, channelName, orderer, chaincode, org1peer0, org1peer0)
|
|
By("initializing chaincode if required")
|
|
if chaincode.InitRequired {
|
|
nwo.InitChaincode(network, channelName, orderer, chaincode, org1peer0)
|
|
}
|
|
|
|
By("querying and invoking the chaincode")
|
|
RunQueryInvokeQuery(network, orderer, org1peer0, channelName, 100)
|
|
Eventually(chaincodeRunner.Buffer()).Should(gbytes.Say("invoking in devmode"))
|
|
|
|
By("killing chaincode process")
|
|
chaincodeProcess.Signal(syscall.SIGKILL)
|
|
Eventually(chaincodeProcess.Wait(), network.EventuallyTimeout).Should(Receive())
|
|
|
|
By("invoking chaincode after it has been killed, expecting it to fail")
|
|
sess, err := network.PeerUserSession(org1peer0, "User1", commands.ChaincodeInvoke{
|
|
ChannelID: channelName,
|
|
Orderer: network.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: "mycc",
|
|
Ctor: `{"Args":["invoke","a","b","10"]}`,
|
|
PeerAddresses: []string{
|
|
network.PeerAddress(network.Peer("Org1", "peer0"), nwo.ListenPort),
|
|
},
|
|
WaitForEvent: true,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, network.EventuallyTimeout).Should(gexec.Exit(1))
|
|
|
|
By("restarting the chaincode process")
|
|
cmd = exec.Command(chaincode.Path, []string{"-peer.address", peerChaincodeAddress}...)
|
|
cmd.Env = append(cmd.Env, envs...)
|
|
chaincodeRunner = ginkgomon.New(ginkgomon.Config{
|
|
Name: "chaincode",
|
|
Command: cmd,
|
|
StartCheckTimeout: 15 * time.Second,
|
|
StartCheck: "starting up in devmode...",
|
|
})
|
|
chaincodeProcess = ifrit.Invoke(chaincodeRunner)
|
|
Eventually(chaincodeProcess.Ready(), network.EventuallyTimeout).Should(BeClosed())
|
|
|
|
By("querying and invoking the chaincode")
|
|
RunQueryInvokeQuery(network, orderer, org1peer0, channelName, 90)
|
|
Eventually(chaincodeRunner).Should(gbytes.Say("invoking in devmode"))
|
|
})
|
|
})
|
|
|
|
func RunQueryInvokeQuery(n *nwo.Network, orderer *nwo.Orderer, peer *nwo.Peer, channel string, queryValue int) {
|
|
By("querying the chaincode")
|
|
sess, err := n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
|
|
ChannelID: channel,
|
|
Name: "mycc",
|
|
Ctor: `{"Args":["query","a"]}`,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess).To(gbytes.Say(strconv.Itoa(queryValue)))
|
|
|
|
By("invoking the chaincode")
|
|
sess, err = n.PeerUserSession(peer, "User1", commands.ChaincodeInvoke{
|
|
ChannelID: channel,
|
|
Orderer: n.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: "mycc",
|
|
Ctor: `{"Args":["invoke","a","b","10"]}`,
|
|
PeerAddresses: []string{
|
|
n.PeerAddress(peer, nwo.ListenPort),
|
|
},
|
|
WaitForEvent: true,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess.Err).To(gbytes.Say("Chaincode invoke successful. result: status:200"))
|
|
|
|
By("querying the chaincode again")
|
|
sess, err = n.PeerUserSession(peer, "User1", commands.ChaincodeQuery{
|
|
ChannelID: channel,
|
|
Name: "mycc",
|
|
Ctor: `{"Args":["query","a"]}`,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
|
|
Expect(sess).To(gbytes.Say(strconv.Itoa(queryValue - 10)))
|
|
}
|
|
|
|
// ApproveChaincodeForMyOrg should only be used when devmode is enabled.
|
|
// It does not set PackageID form ChaincodeApproveForMyOrg command.
|
|
func ApproveChaincodeForMyOrg(n *nwo.Network, channel string, orderer *nwo.Orderer, chaincode nwo.Chaincode, peers ...*nwo.Peer) {
|
|
approvedOrgs := map[string]bool{}
|
|
for _, p := range peers {
|
|
if _, ok := approvedOrgs[p.Organization]; !ok {
|
|
sess, err := n.PeerAdminSession(p, commands.ChaincodeApproveForMyOrg{
|
|
ChannelID: channel,
|
|
Orderer: n.OrdererAddress(orderer, nwo.ListenPort),
|
|
Name: chaincode.Name,
|
|
Version: chaincode.Version,
|
|
Sequence: chaincode.Sequence,
|
|
EndorsementPlugin: chaincode.EndorsementPlugin,
|
|
ValidationPlugin: chaincode.ValidationPlugin,
|
|
SignaturePolicy: chaincode.SignaturePolicy,
|
|
ChannelConfigPolicy: chaincode.ChannelConfigPolicy,
|
|
InitRequired: chaincode.InitRequired,
|
|
CollectionsConfig: chaincode.CollectionsConfig,
|
|
ClientAuth: n.ClientAuthRequired,
|
|
})
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0))
|
|
approvedOrgs[p.Organization] = true
|
|
Eventually(sess.Err, n.EventuallyTimeout).Should(gbytes.Say(`\Qcommitted with status (VALID)\E`))
|
|
}
|
|
}
|
|
}
|
|
|
|
var devModeEtcdraft = &nwo.Config{
|
|
Organizations: []*nwo.Organization{{
|
|
Name: "OrdererOrg",
|
|
MSPID: "OrdererMSP",
|
|
Domain: "example.com",
|
|
EnableNodeOUs: false,
|
|
Users: 0,
|
|
CA: &nwo.CA{Hostname: "ca"},
|
|
}, {
|
|
Name: "Org1",
|
|
MSPID: "Org1MSP",
|
|
Domain: "org1.example.com",
|
|
EnableNodeOUs: true,
|
|
Users: 2,
|
|
CA: &nwo.CA{Hostname: "ca"},
|
|
}},
|
|
Consensus: &nwo.Consensus{
|
|
Type: "etcdraft",
|
|
},
|
|
Orderers: []*nwo.Orderer{
|
|
{Name: "orderer", Organization: "OrdererOrg"},
|
|
},
|
|
Channels: []*nwo.Channel{
|
|
{
|
|
Name: "testchannel",
|
|
Profile: "OneOrgChannel",
|
|
},
|
|
},
|
|
Peers: []*nwo.Peer{{
|
|
Name: "peer0",
|
|
Organization: "Org1",
|
|
Channels: []*nwo.PeerChannel{
|
|
{Name: "testchannel", Anchor: true},
|
|
},
|
|
}},
|
|
Profiles: []*nwo.Profile{{
|
|
Name: "OneOrgChannel",
|
|
Consortium: "SampleConsortium",
|
|
Orderers: []string{"orderer"},
|
|
Organizations: []string{"Org1"},
|
|
}},
|
|
}
|