github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/core/container/container.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package container 8 9 import ( 10 "io" 11 "sync" 12 13 "github.com/hyperledger/fabric/common/flogging" 14 "github.com/hyperledger/fabric/core/chaincode/persistence" 15 "github.com/hyperledger/fabric/core/container/ccintf" 16 17 "github.com/pkg/errors" 18 ) 19 20 var vmLogger = flogging.MustGetLogger("container") 21 22 //go:generate counterfeiter -o mock/vm.go --fake-name VM . VM 23 24 //VM is an abstract virtual image for supporting arbitrary virual machines 25 type VM interface { 26 Build(ccid string, metadata *persistence.ChaincodePackageMetadata, codePackageStream io.Reader) (Instance, error) 27 } 28 29 //go:generate counterfeiter -o mock/instance.go --fake-name Instance . Instance 30 31 // Instance represents a built chaincode instance, because of the docker legacy, calling this a 32 // built 'container' would be very misleading, and going forward with the external launcher 33 // 'image' also seemed inappropriate. So, the vague 'Instance' is used here. 34 type Instance interface { 35 Start(peerConnection *ccintf.PeerConnection) error 36 ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) 37 Stop() error 38 Wait() (int, error) 39 } 40 41 type UninitializedInstance struct{} 42 43 func (UninitializedInstance) Start(peerConnection *ccintf.PeerConnection) error { 44 return errors.Errorf("instance has not yet been built, cannot be started") 45 } 46 47 func (UninitializedInstance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) { 48 return nil, errors.Errorf("instance has not yet been built, cannot get chaincode server info") 49 } 50 51 func (UninitializedInstance) Stop() error { 52 return errors.Errorf("instance has not yet been built, cannot be stopped") 53 } 54 55 func (UninitializedInstance) Wait() (int, error) { 56 return 0, errors.Errorf("instance has not yet been built, cannot wait") 57 } 58 59 //go:generate counterfeiter -o mock/package_provider.go --fake-name PackageProvider . PackageProvider 60 61 // PackageProvider gets chaincode packages from the filesystem. 62 type PackageProvider interface { 63 GetChaincodePackage(packageID string) (*persistence.ChaincodePackageMetadata, io.ReadCloser, error) 64 } 65 66 type Router struct { 67 ExternalVM VM 68 DockerVM VM 69 containers map[string]Instance 70 PackageProvider PackageProvider 71 mutex sync.Mutex 72 } 73 74 func (r *Router) getInstance(ccid string) Instance { 75 r.mutex.Lock() 76 defer r.mutex.Unlock() 77 78 // Note, to resolve the locking problem which existed in the previous code, we never delete 79 // references from the map. In this way, it is safe to release the lock and operate 80 // on the returned reference 81 vm, ok := r.containers[ccid] 82 if !ok { 83 return UninitializedInstance{} 84 } 85 86 return vm 87 } 88 89 func (r *Router) Build(ccid string) error { 90 var instance Instance 91 92 if r.ExternalVM != nil { 93 // for now, the package ID we retrieve from the FS is always the ccid 94 // the chaincode uses for registration 95 metadata, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid) 96 if err != nil { 97 return errors.WithMessage(err, "failed to get chaincode package for external build") 98 } 99 defer codeStream.Close() 100 101 instance, err = r.ExternalVM.Build(ccid, metadata, codeStream) 102 if err != nil { 103 return errors.WithMessage(err, "external builder failed") 104 } 105 } 106 107 if instance == nil { 108 metadata, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid) 109 if err != nil { 110 return errors.WithMessage(err, "failed to get chaincode package for docker build") 111 } 112 defer codeStream.Close() 113 114 instance, err = r.DockerVM.Build(ccid, metadata, codeStream) 115 if err != nil { 116 return errors.WithMessage(err, "docker build failed") 117 } 118 } 119 120 r.mutex.Lock() 121 defer r.mutex.Unlock() 122 if r.containers == nil { 123 r.containers = map[string]Instance{} 124 } 125 r.containers[ccid] = instance 126 127 return nil 128 } 129 130 func (r *Router) ChaincodeServerInfo(ccid string) (*ccintf.ChaincodeServerInfo, error) { 131 return r.getInstance(ccid).ChaincodeServerInfo() 132 } 133 134 func (r *Router) Start(ccid string, peerConnection *ccintf.PeerConnection) error { 135 return r.getInstance(ccid).Start(peerConnection) 136 } 137 138 func (r *Router) Stop(ccid string) error { 139 return r.getInstance(ccid).Stop() 140 } 141 142 func (r *Router) Wait(ccid string) (int, error) { 143 return r.getInstance(ccid).Wait() 144 }