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  }