github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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.DockerVM{} 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 } 160 161 func (si StartImageReq) do(ctxt context.Context, v api.VM) VMCResp { 162 var resp VMCResp 163 164 if err := v.Start(ctxt, si.CCID, si.Args, si.Env, si.Builder); err != nil { 165 resp = VMCResp{Err: err} 166 } else { 167 resp = VMCResp{} 168 } 169 170 return resp 171 } 172 173 func (si StartImageReq) getCCID() ccintf.CCID { 174 return si.CCID 175 } 176 177 //StopImageReq - properties for stopping a container. 178 type StopImageReq struct { 179 ccintf.CCID 180 Timeout uint 181 //by default we will kill the container after stopping 182 Dontkill bool 183 //by default we will remove the container after killing 184 Dontremove bool 185 } 186 187 func (si StopImageReq) do(ctxt context.Context, v api.VM) VMCResp { 188 var resp VMCResp 189 190 if err := v.Stop(ctxt, si.CCID, si.Timeout, si.Dontkill, si.Dontremove); err != nil { 191 resp = VMCResp{Err: err} 192 } else { 193 resp = VMCResp{} 194 } 195 196 return resp 197 } 198 199 func (si StopImageReq) getCCID() ccintf.CCID { 200 return si.CCID 201 } 202 203 //DestroyImageReq - properties for stopping a container. 204 type DestroyImageReq struct { 205 ccintf.CCID 206 Timeout uint 207 Force bool 208 NoPrune bool 209 } 210 211 func (di DestroyImageReq) do(ctxt context.Context, v api.VM) VMCResp { 212 var resp VMCResp 213 214 if err := v.Destroy(ctxt, di.CCID, di.Force, di.NoPrune); err != nil { 215 resp = VMCResp{Err: err} 216 } else { 217 resp = VMCResp{} 218 } 219 220 return resp 221 } 222 223 func (di DestroyImageReq) getCCID() ccintf.CCID { 224 return di.CCID 225 } 226 227 //VMCProcess should be used as follows 228 // . construct a context 229 // . construct req of the right type (e.g., CreateImageReq) 230 // . call it in a go routine 231 // . process response in the go routing 232 //context can be cancelled. VMCProcess will try to cancel calling functions if it can 233 //For instance docker clients api's such as BuildImage are not cancelable. 234 //In all cases VMCProcess will wait for the called go routine to return 235 func VMCProcess(ctxt context.Context, vmtype string, req VMCReqIntf) (interface{}, error) { 236 v := vmcontroller.newVM(vmtype) 237 238 if v == nil { 239 return nil, fmt.Errorf("Unknown VM type %s", vmtype) 240 } 241 242 c := make(chan struct{}) 243 var resp interface{} 244 go func() { 245 defer close(c) 246 247 id, err := v.GetVMName(req.getCCID()) 248 if err != nil { 249 resp = VMCResp{Err: err} 250 return 251 } 252 vmcontroller.lockContainer(id) 253 resp = req.do(ctxt, v) 254 vmcontroller.unlockContainer(id) 255 }() 256 257 select { 258 case <-c: 259 return resp, nil 260 case <-ctxt.Done(): 261 //TODO cancel req.do ... (needed) ? 262 <-c 263 return nil, ctxt.Err() 264 } 265 }