Skip to content

Commit

Permalink
[FAB-16890] move build out of container runtime
Browse files Browse the repository at this point in the history
The build process is common to both container and connector
models and decides which model applies (based on if build
specifies chaincode connection properties). We need to extract
the build out of container runtime.

Change-Id: I3db0cbb98f9d595a2653a042d5e40306b9540d8f
Signed-off-by: muralisr <[email protected]>
  • Loading branch information
muralisrini committed Nov 21, 2019
1 parent fead9d9 commit a9572d6
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 77 deletions.
3 changes: 2 additions & 1 deletion core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const (

// Runtime is used to manage chaincode runtime instances.
type Runtime interface {
Start(ccid string) error
Build(ccid string) (*ccintf.ChaincodeServerInfo, error)
Start(ccid string, ccinfo *ccintf.PeerConnection) error
Stop(ccid string) error
Wait(ccid string) (int, error)
}
Expand Down
17 changes: 9 additions & 8 deletions core/chaincode/chaincode_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import (
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/config"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/container/dockercontroller"
"github.com/hyperledger/fabric/core/ledger"
ledgermock "github.com/hyperledger/fabric/core/ledger/mock"
Expand Down Expand Up @@ -231,14 +232,9 @@ func initMockPeer(channelIDs ...string) (*peer.Peer, *ChaincodeSupport, func(),
}

containerRuntime := &ContainerRuntime{
CACert: ca.CertBytes(),
CertGenerator: certGenerator,
BuildRegistry: buildRegistry,
ContainerRouter: containerRouter,
}
if !globalConfig.TLSEnabled {
containerRuntime.CertGenerator = nil
}
userRunsCC := true
metricsProviders := &disabled.Provider{}
chaincodeHandlerRegistry := NewHandlerRegistry(userRunsCC)
Expand All @@ -247,6 +243,11 @@ func initMockPeer(channelIDs ...string) (*peer.Peer, *ChaincodeSupport, func(),
Runtime: containerRuntime,
Registry: chaincodeHandlerRegistry,
StartupTimeout: globalConfig.StartupTimeout,
CACert: ca.CertBytes(),
CertGenerator: certGenerator,
}
if !globalConfig.TLSEnabled {
chaincodeLauncher.CertGenerator = nil
}
chaincodeSupport := &ChaincodeSupport{
ACLProvider: mockAclProvider,
Expand Down Expand Up @@ -903,7 +904,7 @@ func getHistory(t *testing.T, chainID, ccname string, ccSide *mockpeer.MockCCCom
func TestStartAndWaitSuccess(t *testing.T) {
handlerRegistry := NewHandlerRegistry(false)
fakeRuntime := &mock.Runtime{}
fakeRuntime.StartStub = func(_ string) error {
fakeRuntime.StartStub = func(_ string, _ *ccintf.PeerConnection) error {
handlerRegistry.Ready("testcc:0")
return nil
}
Expand All @@ -925,7 +926,7 @@ func TestStartAndWaitSuccess(t *testing.T) {
//test timeout error
func TestStartAndWaitTimeout(t *testing.T) {
fakeRuntime := &mock.Runtime{}
fakeRuntime.StartStub = func(_ string) error {
fakeRuntime.StartStub = func(_ string, _ *ccintf.PeerConnection) error {
time.Sleep(time.Second)
return nil
}
Expand All @@ -947,7 +948,7 @@ func TestStartAndWaitTimeout(t *testing.T) {
//test container return error
func TestStartAndWaitLaunchError(t *testing.T) {
fakeRuntime := &mock.Runtime{}
fakeRuntime.StartStub = func(_ string) error {
fakeRuntime.StartStub = func(_ string, _ *ccintf.PeerConnection) error {
return errors.New("Bad lunch; upset stomach")
}

Expand Down
45 changes: 10 additions & 35 deletions core/chaincode/container_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,11 @@ SPDX-License-Identifier: Apache-2.0
package chaincode

import (
"github.com/hyperledger/fabric/core/chaincode/accesscontrol"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/pkg/errors"
)

// CertGenerator generates client certificates for chaincode.
type CertGenerator interface {
// Generate returns a certificate and private key and associates
// the hash of the certificates with the given chaincode name
Generate(ccName string) (*accesscontrol.CertAndPrivKeyPair, error)
}

// ContainerRouter is a poor abstraction used for building, and running chaincode processes.
// This management probably does not belong in this package, chaincode process lifecycle should
// be driven by what chaincodes are defined, what chaincodes are instantiated, and not driven by
Expand All @@ -35,29 +27,13 @@ type ContainerRouter interface {

// ContainerRuntime is responsible for managing containerized chaincode.
type ContainerRuntime struct {
CertGenerator CertGenerator
ContainerRouter ContainerRouter
CACert []byte
PeerAddress string
BuildRegistry *container.BuildRegistry
}

// Start launches chaincode in a runtime environment.
func (c *ContainerRuntime) Start(ccid string) error {
var tlsConfig *ccintf.TLSConfig
if c.CertGenerator != nil {
certKeyPair, err := c.CertGenerator.Generate(string(ccid))
if err != nil {
return errors.WithMessagef(err, "failed to generate TLS certificates for %s", ccid)
}

tlsConfig = &ccintf.TLSConfig{
ClientCert: certKeyPair.Cert,
ClientKey: certKeyPair.Key,
RootCert: c.CACert,
}
}

// Build builds the chaincode if necessary and returns ChaincodeServerInfo if
// the chaincode is a server
func (c *ContainerRuntime) Build(ccid string) (*ccintf.ChaincodeServerInfo, error) {
buildStatus, ok := c.BuildRegistry.BuildStatus(ccid)
if !ok {
err := c.ContainerRouter.Build(ccid)
Expand All @@ -66,18 +42,17 @@ func (c *ContainerRuntime) Start(ccid string) error {
<-buildStatus.Done()

if err := buildStatus.Err(); err != nil {
return errors.WithMessage(err, "error building image")
return nil, errors.WithMessage(err, "error building image")
}

return c.ContainerRouter.ChaincodeServerInfo(ccid)
}

// Start launches chaincode in a runtime environment.
func (c *ContainerRuntime) Start(ccid string, ccinfo *ccintf.PeerConnection) error {
chaincodeLogger.Debugf("start container: %s", ccid)

if err := c.ContainerRouter.Start(
ccid,
&ccintf.PeerConnection{
Address: c.PeerAddress,
TLSConfig: tlsConfig,
},
); err != nil {
if err := c.ContainerRouter.Start(ccid, ccinfo); err != nil {
return errors.WithMessage(err, "error starting container")
}

Expand Down
31 changes: 20 additions & 11 deletions core/chaincode/container_runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,39 @@ import (
"github.com/hyperledger/fabric/core/chaincode"
"github.com/hyperledger/fabric/core/chaincode/mock"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

func TestContainerRuntimeStart(t *testing.T) {
func TestContainerRuntimeBuild(t *testing.T) {
fakeRouter := &mock.ContainerRouter{}
fakeRouter.ChaincodeServerInfoReturns(&ccintf.ChaincodeServerInfo{Address: "ccaddress:12345"}, nil)

cr := &chaincode.ContainerRuntime{
ContainerRouter: fakeRouter,
PeerAddress: "peer-address",
BuildRegistry: &container.BuildRegistry{},
}

err := cr.Start("chaincode-name:chaincode-version")
ccinfo, err := cr.Build("chaincode-name:chaincode-version")
assert.NoError(t, err)
assert.Equal(t, &ccintf.ChaincodeServerInfo{Address: "ccaddress:12345"}, ccinfo)

assert.Equal(t, 1, fakeRouter.BuildCallCount())
packageID := fakeRouter.BuildArgsForCall(0)
assert.Equal(t, "chaincode-name:chaincode-version", packageID)
}

func TestContainerRuntimeStart(t *testing.T) {
fakeRouter := &mock.ContainerRouter{}

cr := &chaincode.ContainerRuntime{
ContainerRouter: fakeRouter,
BuildRegistry: &container.BuildRegistry{},
}

err := cr.Start("chaincode-name:chaincode-version", &ccintf.PeerConnection{Address: "peer-address"})
assert.NoError(t, err)

assert.Equal(t, 1, fakeRouter.StartCallCount())
ccid, peerConnection := fakeRouter.StartArgsForCall(0)
Expand All @@ -41,35 +55,30 @@ func TestContainerRuntimeStart(t *testing.T) {

// Try starting a second time, to ensure build is not invoked again
// as the BuildRegistry already holds it
err = cr.Start("chaincode-name:chaincode-version")
err = cr.Start("chaincode-name:chaincode-version", &ccintf.PeerConnection{Address: "fake-address"})
assert.NoError(t, err)
assert.Equal(t, 1, fakeRouter.BuildCallCount())
assert.Equal(t, 2, fakeRouter.StartCallCount())
}

func TestContainerRuntimeStartErrors(t *testing.T) {
tests := []struct {
chaincodeType string
buildErr error
startErr error
errValue string
}{
{pb.ChaincodeSpec_GOLANG.String(), nil, errors.New("process-failed"), "error starting container: process-failed"},
{pb.ChaincodeSpec_GOLANG.String(), errors.New("build-failed"), nil, "error building image: build-failed"},
{pb.ChaincodeSpec_GOLANG.String(), errors.New("build-failed"), nil, "error building image: build-failed"},
{pb.ChaincodeSpec_GOLANG.String(), errors.New("process-failed"), "error starting container: process-failed"},
}

for _, tc := range tests {
fakeRouter := &mock.ContainerRouter{}
fakeRouter.BuildReturns(tc.buildErr)
fakeRouter.StartReturns(tc.startErr)

cr := &chaincode.ContainerRuntime{
ContainerRouter: fakeRouter,
BuildRegistry: &container.BuildRegistry{},
}

err := cr.Start("ccid")
err := cr.Start("ccid", &ccintf.PeerConnection{Address: "fake-address"})
assert.EqualError(t, err, tc.errValue)
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ func initPeer(channelIDs ...string) (*cm.Lifecycle, net.Listener, *ChaincodeSupp
}
containerRuntime := &ContainerRuntime{
BuildRegistry: buildRegistry,
CACert: ca.CertBytes(),
ContainerRouter: containerRouter,
PeerAddress: peerAddress,
}
userRunsCC := false
metricsProviders := &disabled.Provider{}
Expand All @@ -187,6 +185,8 @@ func initPeer(channelIDs ...string) (*cm.Lifecycle, net.Listener, *ChaincodeSupp
Registry: chaincodeHandlerRegistry,
Runtime: containerRuntime,
StartupTimeout: globalConfig.StartupTimeout,
CACert: ca.CertBytes(),
PeerAddress: peerAddress,
}
chaincodeSupport := &ChaincodeSupport{
ACLProvider: aclmgmt.NewACLProvider(
Expand Down
98 changes: 90 additions & 8 deletions core/chaincode/mock/runtime.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a9572d6

Please sign in to comment.