Skip to content

Commit

Permalink
[FAB-16937] complete by hooking CC fwork with ext. CC
Browse files Browse the repository at this point in the history
With the external builder and chaincode support prepared for
chaincode-as-server model this task finally fills in the
remaining pieces to complete the model
. adjust chaincode support for client gRPC stream
. fork runtime to connect to cc server if server info
 is provided

FAB-16937

Change-Id: I09ecb17fcc762e2cb5e706176bec9b6fa738962e
Signed-off-by: muralisr <srinivasan.muralidharan99@gmail.com>
  • Loading branch information
muralisrini authored and denyeart committed Dec 5, 2019
1 parent 4bde8c4 commit 1b8be06
Show file tree
Hide file tree
Showing 13 changed files with 751 additions and 100 deletions.
5 changes: 5 additions & 0 deletions core/chaincode/chaincode_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,8 @@ type policyManager interface {
type policy interface {
policies.Policy
}

//go:generate counterfeiter -o mock/connectionhandler.go --fake-name ConnectionHandler . connectionHandler
type connectionHandler interface {
chaincode.ConnectionHandler
}
5 changes: 3 additions & 2 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/golang/protobuf/proto"
pb "github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/chaincode/extcc"
"github.com/hyperledger/fabric/core/chaincode/lifecycle"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/container/ccintf"
Expand Down Expand Up @@ -42,7 +43,7 @@ type Runtime interface {

// Launcher is used to launch chaincode runtimes.
type Launcher interface {
Launch(ccid string) error
Launch(ccid string, streamHandler extcc.StreamHandler) error
}

// Lifecycle provides a way to retrieve chaincode definitions and the packages necessary to run them
Expand Down Expand Up @@ -79,7 +80,7 @@ func (cs *ChaincodeSupport) Launch(ccid string) (*Handler, error) {
return h, nil
}

if err := cs.Launcher.Launch(ccid); err != nil {
if err := cs.Launcher.Launch(ccid, cs); err != nil {
return nil, errors.Wrapf(err, "could not launch chaincode %s", ccid)
}

Expand Down
9 changes: 6 additions & 3 deletions core/chaincode/chaincode_support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -920,8 +920,9 @@ func TestStartAndWaitSuccess(t *testing.T) {
Metrics: NewLaunchMetrics(&disabled.Provider{}),
}

fakeStreamHandler := &mock.ChaincodeStreamHandler{}
//actual test - everythings good
err := launcher.Launch("testcc:0")
err := launcher.Launch("testcc:0", fakeStreamHandler)
if err != nil {
t.Fatalf("expected success but failed with error %s", err)
}
Expand All @@ -942,8 +943,9 @@ func TestStartAndWaitTimeout(t *testing.T) {
Metrics: NewLaunchMetrics(&disabled.Provider{}),
}

fakeStreamHandler := &mock.ChaincodeStreamHandler{}
//the actual test - timeout 1000 > 500
err := launcher.Launch("testcc:0")
err := launcher.Launch("testcc:0", fakeStreamHandler)
if err == nil {
t.Fatalf("expected error but succeeded")
}
Expand All @@ -963,8 +965,9 @@ func TestStartAndWaitLaunchError(t *testing.T) {
Metrics: NewLaunchMetrics(&disabled.Provider{}),
}

fakeStreamHandler := &mock.ChaincodeStreamHandler{}
//actual test - container launch gives error
err := launcher.Launch("testcc:0")
err := launcher.Launch("testcc:0", fakeStreamHandler)
if err == nil {
t.Fatalf("expected error but succeeded")
}
Expand Down
73 changes: 73 additions & 0 deletions core/chaincode/extcc/extcc_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package extcc

import (
"context"

"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/comm"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/pkg/errors"

pb "github.com/hyperledger/fabric-protos-go/peer"

"google.golang.org/grpc"
)

var extccLogger = flogging.MustGetLogger("extcc")

// StreamHandler handles the `Chaincode` gRPC service with peer as client
type StreamHandler interface {
HandleChaincodeStream(stream ccintf.ChaincodeStream) error
}

type ExternalChaincodeRuntime struct {
}

// createConnection - standard grpc client creating using ClientConfig info (surprised there isn't
// a helper method for this)
func (i *ExternalChaincodeRuntime) createConnection(ccid string, ccinfo *ccintf.ChaincodeServerInfo) (*grpc.ClientConn, error) {
grpcClient, err := comm.NewGRPCClient(ccinfo.ClientConfig)
if err != nil {
return nil, errors.WithMessagef(err, "error creating grpc client to %s", ccid)
}

conn, err := grpcClient.NewConnection(ccinfo.Address)
if err != nil {
return nil, errors.WithMessagef(err, "error creating grpc connection to %s", ccinfo.Address)
}

extccLogger.Debugf("Created external chaincode connection: %s", ccid)

return conn, nil
}

func (i *ExternalChaincodeRuntime) Stream(ccid string, ccinfo *ccintf.ChaincodeServerInfo, sHandler StreamHandler) error {
extccLogger.Debugf("Starting external chaincode connection: %s", ccid)
conn, err := i.createConnection(ccid, ccinfo)
if err != nil {
return errors.WithMessagef(err, "error cannot create connection for %s", ccid)
}

defer conn.Close()

//create the client and start streaming
client := pb.NewChaincodeClient(conn)

stream, err := client.Connect(context.Background())
if err != nil {
return errors.WithMessagef(err, "error creating grpc client connection to %s", ccid)
}

//peer as client has to initiate the stream. Rest of the process is unchanged
sHandler.HandleChaincodeStream(stream)

extccLogger.Debugf("External chaincode %s client exited", ccid)

return nil
}
115 changes: 115 additions & 0 deletions core/chaincode/extcc/extcc_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package extcc_test

import (
"net"
"time"

"github.com/hyperledger/fabric/core/chaincode/extcc"
"github.com/hyperledger/fabric/core/chaincode/extcc/mock"
"github.com/hyperledger/fabric/core/comm"
"github.com/hyperledger/fabric/core/container/ccintf"

"google.golang.org/grpc"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("Extcc", func() {
var (
i *extcc.ExternalChaincodeRuntime
shandler *mock.StreamHandler
)

BeforeEach(func() {
shandler = &mock.StreamHandler{}
i = &extcc.ExternalChaincodeRuntime{}
})

Context("Run", func() {
When("chaincode is running", func() {
var (
cclist net.Listener
ccserv *grpc.Server
)
BeforeEach(func() {
var err error
cclist, err = net.Listen("tcp", "127.0.0.1:0")
Expect(err).To(BeNil())
Expect(cclist).To(Not(BeNil()))
ccserv = grpc.NewServer([]grpc.ServerOption{}...)
go ccserv.Serve(cclist)
})

AfterEach(func() {
if ccserv != nil {
ccserv.Stop()
}
if cclist != nil {
cclist.Close()
}
})

It("runs to completion", func() {
ccinfo := &ccintf.ChaincodeServerInfo{
Address: cclist.Addr().String(),
ClientConfig: comm.ClientConfig{
KaOpts: comm.DefaultKeepaliveOptions,
Timeout: 10 * time.Second,
},
}
err := i.Stream("ccid", ccinfo, shandler)
Expect(err).To(BeNil())
Expect(shandler.HandleChaincodeStreamCallCount()).To(Equal(1))

streamArg := shandler.HandleChaincodeStreamArgsForCall(0)
Expect(streamArg).To(Not(BeNil()))
})
})
Context("chaincode info incorrect", func() {
var (
ccinfo *ccintf.ChaincodeServerInfo
)
BeforeEach(func() {
ccinfo = &ccintf.ChaincodeServerInfo{
Address: "ccaddress:12345",
ClientConfig: comm.ClientConfig{
SecOpts: comm.SecureOptions{
UseTLS: true,
RequireClientCert: true,
Certificate: []byte("fake-cert"),
Key: []byte("fake-key"),
ServerRootCAs: [][]byte{[]byte("fake-root-cert")},
},
Timeout: 10 * time.Second,
},
}
})
When("address is bad", func() {
BeforeEach(func() {
ccinfo.ClientConfig.SecOpts.UseTLS = false
ccinfo.Address = "<badaddress>"
})
It("returns an error", func() {
err := i.Stream("ccid", ccinfo, shandler)
Expect(err).To(MatchError(ContainSubstring("error creating grpc connection to <badaddress>")))
})
})
When("unspecified client spec", func() {
BeforeEach(func() {
ccinfo.ClientConfig.SecOpts.Key = nil
})
It("returns an error", func() {
err := i.Stream("ccid", ccinfo, shandler)
Expect(err).To(MatchError(ContainSubstring("both Key and Certificate are required when using mutual TLS")))
})
})
})
})
})
25 changes: 25 additions & 0 deletions core/chaincode/extcc/extcc_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package extcc_test

import (
"testing"

"github.com/hyperledger/fabric/core/chaincode/extcc"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func TestExtcc(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Chaincode Suite")
}

//go:generate counterfeiter -o mock/ccstreamhandler.go --fake-name StreamHandler . StreamHandler
type chaincodeStreamHandler interface {
extcc.StreamHandler
}
111 changes: 111 additions & 0 deletions core/chaincode/extcc/mock/ccstreamhandler.go

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

0 comments on commit 1b8be06

Please sign in to comment.