github.com/ahlemtn/fabric@v2.1.1+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/docker_builder.go --fake-name DockerBuilder . DockerBuilder 23 24 // DockerBuilder is what is exposed by the dockercontroller 25 type DockerBuilder interface { 26 Build(ccid string, metadata *persistence.ChaincodePackageMetadata, codePackageStream io.Reader) (Instance, error) 27 } 28 29 //go:generate counterfeiter -o mock/external_builder.go --fake-name ExternalBuilder . ExternalBuilder 30 31 // ExternalBuilder is what is exposed by the dockercontroller 32 type ExternalBuilder interface { 33 Build(ccid string, metadata []byte, codePackageStream io.Reader) (Instance, error) 34 } 35 36 //go:generate counterfeiter -o mock/instance.go --fake-name Instance . Instance 37 38 // Instance represents a built chaincode instance, because of the docker legacy, calling this a 39 // built 'container' would be very misleading, and going forward with the external launcher 40 // 'image' also seemed inappropriate. So, the vague 'Instance' is used here. 41 type Instance interface { 42 Start(peerConnection *ccintf.PeerConnection) error 43 ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) 44 Stop() error 45 Wait() (int, error) 46 } 47 48 type UninitializedInstance struct{} 49 50 func (UninitializedInstance) Start(peerConnection *ccintf.PeerConnection) error { 51 return errors.Errorf("instance has not yet been built, cannot be started") 52 } 53 54 func (UninitializedInstance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) { 55 return nil, errors.Errorf("instance has not yet been built, cannot get chaincode server info") 56 } 57 58 func (UninitializedInstance) Stop() error { 59 return errors.Errorf("instance has not yet been built, cannot be stopped") 60 } 61 62 func (UninitializedInstance) Wait() (int, error) { 63 return 0, errors.Errorf("instance has not yet been built, cannot wait") 64 } 65 66 //go:generate counterfeiter -o mock/package_provider.go --fake-name PackageProvider . PackageProvider 67 68 // PackageProvider gets chaincode packages from the filesystem. 69 type PackageProvider interface { 70 GetChaincodePackage(packageID string) (md *persistence.ChaincodePackageMetadata, mdBytes []byte, codeStream io.ReadCloser, err error) 71 } 72 73 type Router struct { 74 ExternalBuilder ExternalBuilder 75 DockerBuilder DockerBuilder 76 containers map[string]Instance 77 PackageProvider PackageProvider 78 mutex sync.Mutex 79 } 80 81 func (r *Router) getInstance(ccid string) Instance { 82 r.mutex.Lock() 83 defer r.mutex.Unlock() 84 85 // Note, to resolve the locking problem which existed in the previous code, we never delete 86 // references from the map. In this way, it is safe to release the lock and operate 87 // on the returned reference 88 vm, ok := r.containers[ccid] 89 if !ok { 90 return UninitializedInstance{} 91 } 92 93 return vm 94 } 95 96 func (r *Router) Build(ccid string) error { 97 var instance Instance 98 99 if r.ExternalBuilder != nil { 100 // for now, the package ID we retrieve from the FS is always the ccid 101 // the chaincode uses for registration 102 _, mdBytes, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid) 103 if err != nil { 104 return errors.WithMessage(err, "failed to get chaincode package for external build") 105 } 106 defer codeStream.Close() 107 108 instance, err = r.ExternalBuilder.Build(ccid, mdBytes, codeStream) 109 if err != nil { 110 return errors.WithMessage(err, "external builder failed") 111 } 112 } 113 114 if instance == nil { 115 if r.DockerBuilder == nil { 116 return errors.New("no DockerBuilder, cannot build") 117 } 118 metadata, _, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid) 119 if err != nil { 120 return errors.WithMessage(err, "failed to get chaincode package for docker build") 121 } 122 defer codeStream.Close() 123 124 instance, err = r.DockerBuilder.Build(ccid, metadata, codeStream) 125 if err != nil { 126 return errors.WithMessage(err, "docker build failed") 127 } 128 } 129 130 r.mutex.Lock() 131 defer r.mutex.Unlock() 132 if r.containers == nil { 133 r.containers = map[string]Instance{} 134 } 135 r.containers[ccid] = instance 136 137 return nil 138 } 139 140 func (r *Router) ChaincodeServerInfo(ccid string) (*ccintf.ChaincodeServerInfo, error) { 141 return r.getInstance(ccid).ChaincodeServerInfo() 142 } 143 144 func (r *Router) Start(ccid string, peerConnection *ccintf.PeerConnection) error { 145 return r.getInstance(ccid).Start(peerConnection) 146 } 147 148 func (r *Router) Stop(ccid string) error { 149 return r.getInstance(ccid).Stop() 150 } 151 152 func (r *Router) Wait(ccid string) (int, error) { 153 return r.getInstance(ccid).Wait() 154 }