github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/container/controller.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package container 18 19 import ( 20 "fmt" 21 "io" 22 "sync" 23 24 "golang.org/x/net/context" 25 26 "github.com/hyperledger/fabric/core/container/api" 27 "github.com/hyperledger/fabric/core/container/ccintf" 28 "github.com/hyperledger/fabric/core/container/dockercontroller" 29 "github.com/hyperledger/fabric/core/container/inproccontroller" 30 ) 31 32 type refCountedLock struct { 33 refCount int 34 lock *sync.RWMutex 35 } 36 37 //VMController - manages VMs 38 // . abstract construction of different types of VMs (we only care about Docker for now) 39 // . manage lifecycle of VM (start with build, start, stop ... 40 // eventually probably need fine grained management) 41 type VMController struct { 42 sync.RWMutex 43 // Handlers for each chaincode 44 containerLocks map[string]*refCountedLock 45 } 46 47 //singleton...acess through NewVMController 48 var vmcontroller *VMController 49 50 //constants for supported containers 51 const ( 52 DOCKER = "Docker" 53 SYSTEM = "System" 54 ) 55 56 //NewVMController - creates/returns singleton 57 func init() { 58 vmcontroller = new(VMController) 59 vmcontroller.containerLocks = make(map[string]*refCountedLock) 60 } 61 62 func (vmc *VMController) newVM(typ string) api.VM { 63 var ( 64 v api.VM 65 ) 66 67 switch typ { 68 case DOCKER: 69 v = dockercontroller.NewDockerVM() 70 case SYSTEM: 71 v = &inproccontroller.InprocVM{} 72 default: 73 v = &dockercontroller.DockerVM{} 74 } 75 return v 76 } 77 78 func (vmc *VMController) lockContainer(id string) { 79 //get the container lock under global lock 80 vmcontroller.Lock() 81 var refLck *refCountedLock 82 var ok bool 83 if refLck, ok = vmcontroller.containerLocks[id]; !ok { 84 refLck = &refCountedLock{refCount: 1, lock: &sync.RWMutex{}} 85 vmcontroller.containerLocks[id] = refLck 86 } else { 87 refLck.refCount++ 88 vmLogger.Debugf("refcount %d (%s)", refLck.refCount, id) 89 } 90 vmcontroller.Unlock() 91 vmLogger.Debugf("waiting for container(%s) lock", id) 92 refLck.lock.Lock() 93 vmLogger.Debugf("got container (%s) lock", id) 94 } 95 96 func (vmc *VMController) unlockContainer(id string) { 97 vmcontroller.Lock() 98 if refLck, ok := vmcontroller.containerLocks[id]; ok { 99 if refLck.refCount <= 0 { 100 panic("refcnt <= 0") 101 } 102 refLck.lock.Unlock() 103 if refLck.refCount--; refLck.refCount == 0 { 104 vmLogger.Debugf("container lock deleted(%s)", id) 105 delete(vmcontroller.containerLocks, id) 106 } 107 } else { 108 vmLogger.Debugf("no lock to unlock(%s)!!", id) 109 } 110 vmcontroller.Unlock() 111 } 112 113 //VMCReqIntf - all requests should implement this interface. 114 //The context should be passed and tested at each layer till we stop 115 //note that we'd stop on the first method on the stack that does not 116 //take context 117 type VMCReqIntf interface { 118 do(ctxt context.Context, v api.VM) VMCResp 119 getCCID() ccintf.CCID 120 } 121 122 //VMCResp - response from requests. resp field is a anon interface. 123 //It can hold any response. err should be tested first 124 type VMCResp struct { 125 Err error 126 Resp interface{} 127 } 128 129 //CreateImageReq - properties for creating an container image 130 type CreateImageReq struct { 131 ccintf.CCID 132 Reader io.Reader 133 Args []string 134 Env []string 135 } 136 137 func (bp CreateImageReq) do(ctxt context.Context, v api.VM) VMCResp { 138 var resp VMCResp 139 140 if err := v.Deploy(ctxt, bp.CCID, bp.Args, bp.Env, bp.Reader); err != nil { 141 resp = VMCResp{Err: err} 142 } else { 143 resp = VMCResp{} 144 } 145 146 return resp 147 } 148 149 func (bp CreateImageReq) getCCID() ccintf.CCID { 150 return bp.CCID 151 } 152 153 //StartImageReq - properties for starting a container. 154 type StartImageReq struct { 155 ccintf.CCID 156 Builder api.BuildSpecFactory 157 Args []string 158 Env []string 159 PrelaunchFunc api.PrelaunchFunc 160 } 161 162 func (si StartImageReq) do(ctxt context.Context, v api.VM) VMCResp { 163 var resp VMCResp 164 165 if err := v.Start(ctxt, si.CCID, si.Args, si.Env, si.Builder, si.PrelaunchFunc); err != nil { 166 resp = VMCResp{Err: err} 167 } else { 168 resp = VMCResp{} 169 } 170 171 return resp 172 } 173 174 func (si StartImageReq) getCCID() ccintf.CCID { 175 return si.CCID 176 } 177 178 //StopImageReq - properties for stopping a container. 179 type StopImageReq struct { 180 ccintf.CCID 181 Timeout uint 182 //by default we will kill the container after stopping 183 Dontkill bool 184 //by default we will remove the container after killing 185 Dontremove bool 186 } 187 188 func (si StopImageReq) do(ctxt context.Context, v api.VM) VMCResp { 189 var resp VMCResp 190 191 if err := v.Stop(ctxt, si.CCID, si.Timeout, si.Dontkill, si.Dontremove); err != nil { 192 resp = VMCResp{Err: err} 193 } else { 194 resp = VMCResp{} 195 } 196 197 return resp 198 } 199 200 func (si StopImageReq) getCCID() ccintf.CCID { 201 return si.CCID 202 } 203 204 //DestroyImageReq - properties for stopping a container. 205 type DestroyImageReq struct { 206 ccintf.CCID 207 Timeout uint 208 Force bool 209 NoPrune bool 210 } 211 212 func (di DestroyImageReq) do(ctxt context.Context, v api.VM) VMCResp { 213 var resp VMCResp 214 215 if err := v.Destroy(ctxt, di.CCID, di.Force, di.NoPrune); err != nil { 216 resp = VMCResp{Err: err} 217 } else { 218 resp = VMCResp{} 219 } 220 221 return resp 222 } 223 224 func (di DestroyImageReq) getCCID() ccintf.CCID { 225 return di.CCID 226 } 227 228 //VMCProcess should be used as follows 229 // . construct a context 230 // . construct req of the right type (e.g., CreateImageReq) 231 // . call it in a go routine 232 // . process response in the go routing 233 //context can be cancelled. VMCProcess will try to cancel calling functions if it can 234 //For instance docker clients api's such as BuildImage are not cancelable. 235 //In all cases VMCProcess will wait for the called go routine to return 236 func VMCProcess(ctxt context.Context, vmtype string, req VMCReqIntf) (interface{}, error) { 237 v := vmcontroller.newVM(vmtype) 238 239 if v == nil { 240 return nil, fmt.Errorf("Unknown VM type %s", vmtype) 241 } 242 243 c := make(chan struct{}) 244 var resp interface{} 245 go func() { 246 defer close(c) 247 248 id, err := v.GetVMName(req.getCCID(), nil) 249 if err != nil { 250 resp = VMCResp{Err: err} 251 return 252 } 253 vmcontroller.lockContainer(id) 254 resp = req.do(ctxt, v) 255 vmcontroller.unlockContainer(id) 256 }() 257 258 select { 259 case <-c: 260 return resp, nil 261 case <-ctxt.Done(): 262 //TODO cancel req.do ... (needed) ? 263 <-c 264 return nil, ctxt.Err() 265 } 266 }