Skip to content

Commit

Permalink
[FAB-678] Add BuildSpecFactory to container controller
Browse files Browse the repository at this point in the history
We currently take an io.Reader as a parameter to
Controller.Start().  This is relatively inconsequential since the
docker tarball is precomputed.  However, in the near future, we
will be splitting this up so that cds.CodePackage is only a src
container.  The formulation into a docker container will happen
JIT in response to a peer trying to start the container.  Once
this happens, we want to avoid the overhead of computing the
build specification unless it is actually needed.  Therefore,
we use a callback that the docker-controller may use to indicate
when the build-spec is needed.

Part of FAB-678

Change-Id: I259c9c0b600866c2b78c4319039ca8214d2a9620
Signed-off-by: Greg Haskins <gregory.haskins@gmail.com>
  • Loading branch information
ghaskins committed Feb 2, 2017
1 parent b978f0d commit e1e4efc
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 27 deletions.
10 changes: 6 additions & 4 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
"github.com/hyperledger/fabric/core/chaincode/shim"
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/api"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/ledger"
pb "github.com/hyperledger/fabric/protos/peer"
Expand Down Expand Up @@ -355,7 +356,7 @@ func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cccid *ccprovider.CCCont
}

// launchAndWaitForRegister will launch container if not already running. Use the targz to create the image if not found
func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec, cLang pb.ChaincodeSpec_Type, targz io.Reader) error {
func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.Context, cccid *ccprovider.CCContext, cds *pb.ChaincodeDeploymentSpec, cLang pb.ChaincodeSpec_Type, builder api.BuildSpecFactory) error {
canName := cccid.GetCanonicalName()
if canName == "" {
return fmt.Errorf("chaincode name not set")
Expand Down Expand Up @@ -384,7 +385,7 @@ func (chaincodeSupport *ChaincodeSupport) launchAndWaitForRegister(ctxt context.

vmtype, _ := chaincodeSupport.getVMType(cds)

sir := container.StartImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, ChainID: cccid.ChainID, Version: cccid.Version}, Reader: targz, Args: args, Env: env}
sir := container.StartImageReq{CCID: ccintf.CCID{ChaincodeSpec: cds.ChaincodeSpec, NetworkID: chaincodeSupport.peerNetworkID, PeerID: chaincodeSupport.peerID, ChainID: cccid.ChainID, Version: cccid.Version}, Builder: builder, Args: args, Env: env}

ipcCtxt := context.WithValue(ctxt, ccintf.GetCCHandlerKey(), chaincodeSupport)

Expand Down Expand Up @@ -538,8 +539,9 @@ func (chaincodeSupport *ChaincodeSupport) Launch(context context.Context, cccid

//launch container if it is a System container or not in dev mode
if (!chaincodeSupport.userRunsCC || cds.ExecEnv == pb.ChaincodeDeploymentSpec_SYSTEM) && (chrte == nil || chrte.handler == nil) {
var targz io.Reader = bytes.NewBuffer(cds.CodePackage)
err = chaincodeSupport.launchAndWaitForRegister(context, cccid, cds, cLang, targz)

builder := func() (io.Reader, error) { return bytes.NewBuffer(cds.CodePackage), nil }
err = chaincodeSupport.launchAndWaitForRegister(context, cccid, cds, cLang, builder)
if err != nil {
chaincodeLogger.Errorf("launchAndWaitForRegister failed %s", err)
return cID, cMsg, err
Expand Down
36 changes: 36 additions & 0 deletions core/container/api/core.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Copyright IBM Corp. 2017 All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package api

import (
"io"

"golang.org/x/net/context"

"github.com/hyperledger/fabric/core/container/ccintf"
)

type BuildSpecFactory func() (io.Reader, error)

//abstract virtual image for supporting arbitrary virual machines
type VM interface {
Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error
Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder BuildSpecFactory) error
Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error
Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error
GetVMName(ccID ccintf.CCID) (string, error)
}
32 changes: 12 additions & 20 deletions core/container/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,12 @@ import (

"golang.org/x/net/context"

"github.com/hyperledger/fabric/core/container/api"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/hyperledger/fabric/core/container/dockercontroller"
"github.com/hyperledger/fabric/core/container/inproccontroller"
)

//abstract virtual image for supporting arbitrary virual machines
type vm interface {
Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error
Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error
Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error
Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error
GetVMName(ccID ccintf.CCID) (string, error)
}

type refCountedLock struct {
refCount int
lock *sync.RWMutex
Expand Down Expand Up @@ -67,9 +59,9 @@ func init() {
vmcontroller.containerLocks = make(map[string]*refCountedLock)
}

func (vmc *VMController) newVM(typ string) vm {
func (vmc *VMController) newVM(typ string) api.VM {
var (
v vm
v api.VM
)

switch typ {
Expand Down Expand Up @@ -123,7 +115,7 @@ func (vmc *VMController) unlockContainer(id string) {
//note that we'd stop on the first method on the stack that does not
//take context
type VMCReqIntf interface {
do(ctxt context.Context, v vm) VMCResp
do(ctxt context.Context, v api.VM) VMCResp
getCCID() ccintf.CCID
}

Expand All @@ -142,7 +134,7 @@ type CreateImageReq struct {
Env []string
}

func (bp CreateImageReq) do(ctxt context.Context, v vm) VMCResp {
func (bp CreateImageReq) do(ctxt context.Context, v api.VM) VMCResp {
var resp VMCResp

if err := v.Deploy(ctxt, bp.CCID, bp.Args, bp.Env, bp.Reader); err != nil {
Expand All @@ -161,15 +153,15 @@ func (bp CreateImageReq) getCCID() ccintf.CCID {
//StartImageReq - properties for starting a container.
type StartImageReq struct {
ccintf.CCID
Reader io.Reader
Args []string
Env []string
Builder api.BuildSpecFactory
Args []string
Env []string
}

func (si StartImageReq) do(ctxt context.Context, v vm) VMCResp {
func (si StartImageReq) do(ctxt context.Context, v api.VM) VMCResp {
var resp VMCResp

if err := v.Start(ctxt, si.CCID, si.Args, si.Env, si.Reader); err != nil {
if err := v.Start(ctxt, si.CCID, si.Args, si.Env, si.Builder); err != nil {
resp = VMCResp{Err: err}
} else {
resp = VMCResp{}
Expand All @@ -192,7 +184,7 @@ type StopImageReq struct {
Dontremove bool
}

func (si StopImageReq) do(ctxt context.Context, v vm) VMCResp {
func (si StopImageReq) do(ctxt context.Context, v api.VM) VMCResp {
var resp VMCResp

if err := v.Stop(ctxt, si.CCID, si.Timeout, si.Dontkill, si.Dontremove); err != nil {
Expand All @@ -216,7 +208,7 @@ type DestroyImageReq struct {
NoPrune bool
}

func (di DestroyImageReq) do(ctxt context.Context, v vm) VMCResp {
func (di DestroyImageReq) do(ctxt context.Context, v api.VM) VMCResp {
var resp VMCResp

if err := v.Destroy(ctxt, di.CCID, di.Force, di.NoPrune); err != nil {
Expand Down
11 changes: 9 additions & 2 deletions core/container/dockercontroller/dockercontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"bufio"

"github.com/fsouza/go-dockerclient"
container "github.com/hyperledger/fabric/core/container/api"
"github.com/hyperledger/fabric/core/container/ccintf"
cutil "github.com/hyperledger/fabric/core/container/util"
"github.com/op/go-logging"
Expand Down Expand Up @@ -154,7 +155,7 @@ func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string
}

//Start starts a container using a previously created docker image
func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory) error {
imageID, _ := vm.GetVMName(ccid)
client, err := cutil.NewDockerClient()
if err != nil {
Expand All @@ -174,8 +175,14 @@ func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string,
if err != nil {
//if image not found try to create image and retry
if err == docker.ErrNoSuchImage {
if reader != nil {
if builder != nil {
dockerLogger.Debugf("start-could not find image ...attempt to recreate image %s", err)

reader, err := builder()
if err != nil {
dockerLogger.Errorf("Error creating image builder: %s", err)
}

if err = vm.deployImage(client, ccid, args, env, reader); err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion core/container/inproccontroller/inproccontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"io"

"github.com/hyperledger/fabric/core/chaincode/shim"
container "github.com/hyperledger/fabric/core/container/api"
"github.com/hyperledger/fabric/core/container/ccintf"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/op/go-logging"
Expand Down Expand Up @@ -153,7 +154,7 @@ func (ipc *inprocContainer) launchInProc(ctxt context.Context, id string, args [
}

//Start starts a previously registered system codechain
func (vm *InprocVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error {
func (vm *InprocVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory) error {
path := ccid.ChaincodeSpec.ChaincodeID.Path

ipctemplate := typeRegistry[path]
Expand Down

0 comments on commit e1e4efc

Please sign in to comment.