github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/core/container/inproccontroller/inproccontroller.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 inproccontroller 18 19 import ( 20 "fmt" 21 "io" 22 23 "github.com/hyperledger/fabric/core/chaincode/shim" 24 container "github.com/hyperledger/fabric/core/container/api" 25 "github.com/hyperledger/fabric/core/container/ccintf" 26 pb "github.com/hyperledger/fabric/protos/peer" 27 "github.com/op/go-logging" 28 29 "golang.org/x/net/context" 30 ) 31 32 type inprocContainer struct { 33 chaincode shim.Chaincode 34 running bool 35 args []string 36 env []string 37 stopChan chan struct{} 38 } 39 40 var ( 41 inprocLogger = logging.MustGetLogger("inproccontroller") 42 typeRegistry = make(map[string]*inprocContainer) 43 instRegistry = make(map[string]*inprocContainer) 44 ) 45 46 // errors 47 48 //SysCCRegisteredErr registered error 49 type SysCCRegisteredErr string 50 51 func (s SysCCRegisteredErr) Error() string { 52 return fmt.Sprintf("%s already registered", string(s)) 53 } 54 55 //Register registers system chaincode with given path. The deploy should be called to initialize 56 func Register(path string, cc shim.Chaincode) error { 57 tmp := typeRegistry[path] 58 if tmp != nil { 59 return SysCCRegisteredErr(path) 60 } 61 62 typeRegistry[path] = &inprocContainer{chaincode: cc} 63 return nil 64 } 65 66 //InprocVM is a vm. It is identified by a executable name 67 type InprocVM struct { 68 id string 69 } 70 71 func (vm *InprocVM) getInstance(ctxt context.Context, ipctemplate *inprocContainer, instName string, args []string, env []string) (*inprocContainer, error) { 72 ipc := instRegistry[instName] 73 if ipc != nil { 74 inprocLogger.Warningf("chaincode instance exists for %s", instName) 75 return ipc, nil 76 } 77 ipc = &inprocContainer{args: args, env: env, chaincode: ipctemplate.chaincode, stopChan: make(chan struct{})} 78 instRegistry[instName] = ipc 79 inprocLogger.Debugf("chaincode instance created for %s", instName) 80 return ipc, nil 81 } 82 83 //Deploy verifies chaincode is registered and creates an instance for it. Currently only one instance can be created 84 func (vm *InprocVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error { 85 path := ccid.ChaincodeSpec.ChaincodeId.Path 86 87 ipctemplate := typeRegistry[path] 88 if ipctemplate == nil { 89 return fmt.Errorf(fmt.Sprintf("%s not registered. Please register the system chaincode in inprocinstances.go", path)) 90 } 91 92 if ipctemplate.chaincode == nil { 93 return fmt.Errorf(fmt.Sprintf("%s system chaincode does not contain chaincode instance", path)) 94 } 95 96 instName, _ := vm.GetVMName(ccid) 97 _, err := vm.getInstance(ctxt, ipctemplate, instName, args, env) 98 99 //FUTURE ... here is where we might check code for safety 100 inprocLogger.Debugf("registered : %s", path) 101 102 return err 103 } 104 105 func (ipc *inprocContainer) launchInProc(ctxt context.Context, id string, args []string, env []string, ccSupport ccintf.CCSupport) error { 106 peerRcvCCSend := make(chan *pb.ChaincodeMessage) 107 ccRcvPeerSend := make(chan *pb.ChaincodeMessage) 108 var err error 109 ccchan := make(chan struct{}, 1) 110 ccsupportchan := make(chan struct{}, 1) 111 go func() { 112 defer close(ccchan) 113 inprocLogger.Debugf("chaincode started for %s", id) 114 if args == nil { 115 args = ipc.args 116 } 117 if env == nil { 118 env = ipc.env 119 } 120 err := shim.StartInProc(env, args, ipc.chaincode, ccRcvPeerSend, peerRcvCCSend) 121 if err != nil { 122 err = fmt.Errorf("chaincode-support ended with err: %s", err) 123 inprocLogger.Errorf("%s", err) 124 } 125 inprocLogger.Debugf("chaincode ended with for %s with err: %s", id, err) 126 }() 127 128 go func() { 129 defer close(ccsupportchan) 130 inprocStream := newInProcStream(peerRcvCCSend, ccRcvPeerSend) 131 inprocLogger.Debugf("chaincode-support started for %s", id) 132 err := ccSupport.HandleChaincodeStream(ctxt, inprocStream) 133 if err != nil { 134 err = fmt.Errorf("chaincode ended with err: %s", err) 135 inprocLogger.Errorf("%s", err) 136 } 137 inprocLogger.Debugf("chaincode-support ended with for %s with err: %s", id, err) 138 }() 139 140 select { 141 case <-ccchan: 142 close(peerRcvCCSend) 143 inprocLogger.Debugf("chaincode %s quit", id) 144 case <-ccsupportchan: 145 close(ccRcvPeerSend) 146 inprocLogger.Debugf("chaincode support %s quit", id) 147 case <-ipc.stopChan: 148 close(ccRcvPeerSend) 149 close(peerRcvCCSend) 150 inprocLogger.Debugf("chaincode %s stopped", id) 151 } 152 153 return err 154 } 155 156 //Start starts a previously registered system codechain 157 func (vm *InprocVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory) error { 158 path := ccid.ChaincodeSpec.ChaincodeId.Path 159 160 ipctemplate := typeRegistry[path] 161 162 if ipctemplate == nil { 163 return fmt.Errorf(fmt.Sprintf("%s not registered", path)) 164 } 165 166 instName, _ := vm.GetVMName(ccid) 167 168 ipc, err := vm.getInstance(ctxt, ipctemplate, instName, args, env) 169 170 if err != nil { 171 return fmt.Errorf(fmt.Sprintf("could not create instance for %s", instName)) 172 } 173 174 if ipc.running { 175 return fmt.Errorf(fmt.Sprintf("chaincode running %s", path)) 176 } 177 178 //TODO VALIDITY CHECKS ? 179 180 ccSupport, ok := ctxt.Value(ccintf.GetCCHandlerKey()).(ccintf.CCSupport) 181 if !ok || ccSupport == nil { 182 return fmt.Errorf("in-process communication generator not supplied") 183 } 184 185 ipc.running = true 186 187 go func() { 188 defer func() { 189 if r := recover(); r != nil { 190 inprocLogger.Criticalf("caught panic from chaincode %s", instName) 191 } 192 }() 193 ipc.launchInProc(ctxt, instName, args, env, ccSupport) 194 }() 195 196 return nil 197 } 198 199 //Stop stops a system codechain 200 func (vm *InprocVM) Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error { 201 path := ccid.ChaincodeSpec.ChaincodeId.Path 202 203 ipctemplate := typeRegistry[path] 204 if ipctemplate == nil { 205 return fmt.Errorf("%s not registered", path) 206 } 207 208 instName, _ := vm.GetVMName(ccid) 209 210 ipc := instRegistry[instName] 211 212 if ipc == nil { 213 return fmt.Errorf("%s not found", instName) 214 } 215 216 if !ipc.running { 217 return fmt.Errorf("%s not running", instName) 218 } 219 220 ipc.stopChan <- struct{}{} 221 222 delete(instRegistry, instName) 223 //TODO stop 224 return nil 225 } 226 227 //Destroy destroys an image 228 func (vm *InprocVM) Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, noprune bool) error { 229 //not implemented 230 return nil 231 } 232 233 //GetVMName ignores the peer and network name as it just needs to be unique in process 234 func (vm *InprocVM) GetVMName(ccid ccintf.CCID) (string, error) { 235 return ccid.GetName(), nil 236 }