github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/core/chaincode/platforms/golang/hash.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 golang
    18  
    19  import (
    20  	"archive/tar"
    21  	"bytes"
    22  	"encoding/hex"
    23  	"errors"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"os/exec"
    28  	"path/filepath"
    29  	"strings"
    30  	"time"
    31  
    32  	"github.com/golang/protobuf/proto"
    33  	"github.com/op/go-logging"
    34  	"github.com/spf13/viper"
    35  
    36  	"github.com/hyperledger/fabric/common/util"
    37  	ccutil "github.com/hyperledger/fabric/core/chaincode/platforms/util"
    38  	pb "github.com/hyperledger/fabric/protos/peer"
    39  )
    40  
    41  var logger = logging.MustGetLogger("golang/hash")
    42  
    43  func getCodeFromHTTP(path string) (codegopath string, err error) {
    44  	codegopath = ""
    45  	err = nil
    46  	logger.Debugf("getCodeFromHTTP %s", path)
    47  
    48  	// The following could be done with os.Getenv("GOPATH") but we need to change it later so this prepares for that next step
    49  	env := os.Environ()
    50  	var origgopath string
    51  	var gopathenvIndex int
    52  	for i, v := range env {
    53  		if strings.Index(v, "GOPATH=") == 0 {
    54  			p := strings.SplitAfter(v, "GOPATH=")
    55  			origgopath = p[1]
    56  			gopathenvIndex = i
    57  			break
    58  		}
    59  	}
    60  	if origgopath == "" {
    61  		err = errors.New("GOPATH not defined")
    62  		return
    63  	}
    64  	// Only take the first element of GOPATH
    65  	gopath := filepath.SplitList(origgopath)[0]
    66  
    67  	// Define a new gopath in which to download the code
    68  	newgopath := filepath.Join(gopath, "_usercode_")
    69  
    70  	//ignore errors.. _usercode_ might exist. TempDir will catch any other errors
    71  	os.Mkdir(newgopath, 0755)
    72  
    73  	if codegopath, err = ioutil.TempDir(newgopath, ""); err != nil {
    74  		err = fmt.Errorf("could not create tmp dir under %s(%s)", newgopath, err)
    75  		return
    76  	}
    77  
    78  	//go paths can have multiple dirs. We create a GOPATH with two source tree's as follows
    79  	//
    80  	//    <temporary empty folder to download chaincode source> : <local go path with OBC source>
    81  	//
    82  	//This approach has several goodness:
    83  	// . Go will pick the first path to download user code (which we will delete after processing)
    84  	// . GO will not download OBC as it is in the second path. GO will use the local OBC for generating chaincode image
    85  	//     . network savings
    86  	//     . more secure
    87  	//     . as we are not downloading OBC, private, password-protected OBC repo's become non-issue
    88  
    89  	env[gopathenvIndex] = "GOPATH=" + codegopath + string(os.PathListSeparator) + origgopath
    90  
    91  	// Use a 'go get' command to pull the chaincode from the given repo
    92  	logger.Debugf("go get %s", path)
    93  	cmd := exec.Command("go", "get", path)
    94  	cmd.Env = env
    95  	var out bytes.Buffer
    96  	cmd.Stdout = &out
    97  	var errBuf bytes.Buffer
    98  	cmd.Stderr = &errBuf //capture Stderr and print it on error
    99  	err = cmd.Start()
   100  
   101  	// Create a go routine that will wait for the command to finish
   102  	done := make(chan error, 1)
   103  	go func() {
   104  		done <- cmd.Wait()
   105  	}()
   106  
   107  	select {
   108  	case <-time.After(time.Duration(viper.GetInt("chaincode.deploytimeout")) * time.Millisecond):
   109  		// If pulling repos takes too long, we should give up
   110  		// (This can happen if a repo is private and the git clone asks for credentials)
   111  		if err = cmd.Process.Kill(); err != nil {
   112  			err = fmt.Errorf("failed to kill: %s", err)
   113  		} else {
   114  			err = errors.New("Getting chaincode took too long")
   115  		}
   116  	case err = <-done:
   117  		// If we're here, the 'go get' command must have finished
   118  		if err != nil {
   119  			err = fmt.Errorf("'go get' failed with error: \"%s\"\n%s", err, string(errBuf.Bytes()))
   120  		}
   121  	}
   122  	return
   123  }
   124  
   125  func getCodeFromFS(path string) (codegopath string, err error) {
   126  	logger.Debugf("getCodeFromFS %s", path)
   127  	gopath := os.Getenv("GOPATH")
   128  	if gopath == "" {
   129  		err = errors.New("GOPATH not defined")
   130  		return
   131  	}
   132  	// Only take the first element of GOPATH
   133  	codegopath = filepath.SplitList(gopath)[0]
   134  
   135  	return
   136  }
   137  
   138  //collectChaincodeFiles collects chaincode files and generates hashcode for the
   139  //package. If path is a HTTP(s) url it downloads the code first.
   140  //NOTE: for dev mode, user builds and runs chaincode manually. The name provided
   141  //by the user is equivalent to the path. This method will treat the name
   142  //as codebytes and compute the hash from it. ie, user cannot run the chaincode
   143  //with the same (name, input, args)
   144  func collectChaincodeFiles(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) {
   145  	if spec == nil {
   146  		return "", errors.New("Cannot collect files from nil spec")
   147  	}
   148  
   149  	chaincodeID := spec.ChaincodeId
   150  	if chaincodeID == nil || chaincodeID.Path == "" {
   151  		return "", errors.New("Cannot collect files from empty chaincode path")
   152  	}
   153  
   154  	//install will not have inputs and we don't have to collect hash for it
   155  	var inputbytes []byte
   156  
   157  	var err error
   158  	if spec.Input == nil || len(spec.Input.Args) == 0 {
   159  		logger.Debugf("not using input for hash computation for %v ", chaincodeID)
   160  	} else {
   161  		inputbytes, err = proto.Marshal(spec.Input)
   162  		if err != nil {
   163  			return "", fmt.Errorf("Error marshalling constructor: %s", err)
   164  		}
   165  	}
   166  
   167  	//code root will point to the directory where the code exists
   168  	//in the case of http it will be a temporary dir that
   169  	//will have to be deleted
   170  	var codegopath string
   171  
   172  	var ishttp bool
   173  	defer func() {
   174  		if ishttp && codegopath != "" {
   175  			os.RemoveAll(codegopath)
   176  		}
   177  	}()
   178  
   179  	path := chaincodeID.Path
   180  
   181  	var actualcodepath string
   182  	if strings.HasPrefix(path, "http://") {
   183  		ishttp = true
   184  		actualcodepath = path[7:]
   185  		codegopath, err = getCodeFromHTTP(actualcodepath)
   186  	} else if strings.HasPrefix(path, "https://") {
   187  		ishttp = true
   188  		actualcodepath = path[8:]
   189  		codegopath, err = getCodeFromHTTP(actualcodepath)
   190  	} else {
   191  		actualcodepath = path
   192  		codegopath, err = getCodeFromFS(path)
   193  	}
   194  
   195  	if err != nil {
   196  		return "", fmt.Errorf("Error getting code %s", err)
   197  	}
   198  
   199  	tmppath := filepath.Join(codegopath, "src", actualcodepath)
   200  	if err = ccutil.IsCodeExist(tmppath); err != nil {
   201  		return "", fmt.Errorf("code does not exist %s", err)
   202  	}
   203  
   204  	hash := []byte{}
   205  	if inputbytes != nil {
   206  		hash = util.GenerateHashFromSignature(actualcodepath, inputbytes)
   207  	}
   208  
   209  	hash, err = ccutil.HashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw)
   210  	if err != nil {
   211  		return "", fmt.Errorf("Could not get hashcode for %s - %s\n", path, err)
   212  	}
   213  
   214  	return hex.EncodeToString(hash[:]), nil
   215  }