diff --git a/core/chaincode/chaincode_support.go b/core/chaincode/chaincode_support.go index a8c71e4392f..55c1701643c 100644 --- a/core/chaincode/chaincode_support.go +++ b/core/chaincode/chaincode_support.go @@ -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" @@ -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") @@ -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) @@ -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 diff --git a/core/container/api/core.go b/core/container/api/core.go new file mode 100644 index 00000000000..9a1b1722a03 --- /dev/null +++ b/core/container/api/core.go @@ -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) +} diff --git a/core/container/controller.go b/core/container/controller.go index aa1a09056b9..9f771f459cc 100644 --- a/core/container/controller.go +++ b/core/container/controller.go @@ -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 @@ -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 { @@ -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 } @@ -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 { @@ -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{} @@ -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 { @@ -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 { diff --git a/core/container/dockercontroller/dockercontroller.go b/core/container/dockercontroller/dockercontroller.go index 126161c2f56..2667ebe7cc7 100644 --- a/core/container/dockercontroller/dockercontroller.go +++ b/core/container/dockercontroller/dockercontroller.go @@ -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" @@ -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 { @@ -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 } diff --git a/core/container/inproccontroller/inproccontroller.go b/core/container/inproccontroller/inproccontroller.go index 1cbf9fe14b7..f39d1712048 100644 --- a/core/container/inproccontroller/inproccontroller.go +++ b/core/container/inproccontroller/inproccontroller.go @@ -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" @@ -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]