github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/core/chaincode/platforms/util/utils.go (about)

     1  package util
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  
    11  	"io"
    12  
    13  	"github.com/fsouza/go-dockerclient"
    14  	"github.com/hyperledger/fabric/common/util"
    15  	cutil "github.com/hyperledger/fabric/core/container/util"
    16  	"github.com/op/go-logging"
    17  )
    18  
    19  var logger = logging.MustGetLogger("util")
    20  
    21  //ComputeHash computes contents hash based on previous hash
    22  func ComputeHash(contents []byte, hash []byte) []byte {
    23  	newSlice := make([]byte, len(hash)+len(contents))
    24  
    25  	//copy the contents
    26  	copy(newSlice[0:len(contents)], contents[:])
    27  
    28  	//add the previous hash
    29  	copy(newSlice[len(contents):], hash[:])
    30  
    31  	//compute new hash
    32  	hash = util.ComputeSHA256(newSlice)
    33  
    34  	return hash
    35  }
    36  
    37  //HashFilesInDir computes h=hash(h,file bytes) for each file in a directory
    38  //Directory entries are traversed recursively. In the end a single
    39  //hash value is returned for the entire directory structure
    40  func HashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) {
    41  	currentDir := filepath.Join(rootDir, dir)
    42  	logger.Debugf("hashFiles %s", currentDir)
    43  	//ReadDir returns sorted list of files in dir
    44  	fis, err := ioutil.ReadDir(currentDir)
    45  	if err != nil {
    46  		return hash, fmt.Errorf("ReadDir failed %s\n", err)
    47  	}
    48  	for _, fi := range fis {
    49  		name := filepath.Join(dir, fi.Name())
    50  		if fi.IsDir() {
    51  			var err error
    52  			hash, err = HashFilesInDir(rootDir, name, hash, tw)
    53  			if err != nil {
    54  				return hash, err
    55  			}
    56  			continue
    57  		}
    58  		fqp := filepath.Join(rootDir, name)
    59  		buf, err := ioutil.ReadFile(fqp)
    60  		if err != nil {
    61  			logger.Errorf("Error reading %s\n", err)
    62  			return hash, err
    63  		}
    64  
    65  		//get the new hash from file contents
    66  		hash = ComputeHash(buf, hash)
    67  
    68  		if tw != nil {
    69  			is := bytes.NewReader(buf)
    70  			if err = cutil.WriteStreamToPackage(is, fqp, filepath.Join("src", name), tw); err != nil {
    71  				return hash, fmt.Errorf("Error adding file to tar %s", err)
    72  			}
    73  		}
    74  	}
    75  	return hash, nil
    76  }
    77  
    78  //IsCodeExist checks the chaincode if exists
    79  func IsCodeExist(tmppath string) error {
    80  	file, err := os.Open(tmppath)
    81  	if err != nil {
    82  		return fmt.Errorf("Could not open file %s", err)
    83  	}
    84  
    85  	fi, err := file.Stat()
    86  	if err != nil {
    87  		return fmt.Errorf("Could not stat file %s", err)
    88  	}
    89  
    90  	if !fi.IsDir() {
    91  		return fmt.Errorf("File %s is not dir\n", file.Name())
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  type DockerBuildOptions struct {
    98  	Image        string
    99  	Env          []string
   100  	Cmd          string
   101  	InputStream  io.Reader
   102  	OutputStream io.Writer
   103  }
   104  
   105  //-------------------------------------------------------------------------------------------
   106  // DockerBuild
   107  //-------------------------------------------------------------------------------------------
   108  // This function allows a "pass-through" build of chaincode within a docker container as
   109  // an alternative to using standard "docker build" + Dockerfile mechanisms.  The plain docker
   110  // build is somewhat limiting due to the resulting image that is a superset composition of
   111  // the build-time and run-time environments.  This superset can be problematic on several
   112  // fronts, such as a bloated image size, and additional security exposure associated with
   113  // applications that are not needed, etc.
   114  //
   115  // Therefore, this mechanism creates a pipeline consisting of an ephemeral docker
   116  // container that accepts source code as input, runs some function (e.g. "go build"), and
   117  // outputs the result.  The intention is that this output will be consumed as the basis of
   118  // a streamlined container by installing the output into a downstream docker-build based on
   119  // an appropriate minimal image.
   120  //
   121  // The input parameters are fairly simple:
   122  //      - Image:        (optional) The builder image to use or "chaincode.builder"
   123  //      - Env:          (optional) environment variables for the build environment.
   124  //      - Cmd:          The command to execute inside the container.
   125  //      - InputStream:  A tarball of files that will be expanded into /chaincode/input.
   126  //      - OutputStream: A tarball of files that will be gathered from /chaincode/output
   127  //                      after successful execution of Cmd.
   128  //-------------------------------------------------------------------------------------------
   129  func DockerBuild(opts DockerBuildOptions) error {
   130  	client, err := cutil.NewDockerClient()
   131  	if err != nil {
   132  		return fmt.Errorf("Error creating docker client: %s", err)
   133  	}
   134  
   135  	if opts.Image == "" {
   136  		opts.Image = cutil.GetDockerfileFromConfig("chaincode.builder")
   137  		if opts.Image == "" {
   138  			return fmt.Errorf("No image provided and \"chaincode.builder\" default does not exist")
   139  		}
   140  	}
   141  
   142  	//-----------------------------------------------------------------------------------
   143  	// Create an ephemeral container, armed with our Env/Cmd
   144  	//-----------------------------------------------------------------------------------
   145  	container, err := client.CreateContainer(docker.CreateContainerOptions{
   146  		Config: &docker.Config{
   147  			Image:        opts.Image,
   148  			Env:          opts.Env,
   149  			Cmd:          []string{"/bin/sh", "-c", opts.Cmd},
   150  			AttachStdout: true,
   151  			AttachStderr: true,
   152  		},
   153  	})
   154  	if err != nil {
   155  		return fmt.Errorf("Error creating container: %s", err)
   156  	}
   157  	defer client.RemoveContainer(docker.RemoveContainerOptions{ID: container.ID})
   158  
   159  	//-----------------------------------------------------------------------------------
   160  	// Upload our input stream
   161  	//-----------------------------------------------------------------------------------
   162  	err = client.UploadToContainer(container.ID, docker.UploadToContainerOptions{
   163  		Path:        "/chaincode/input",
   164  		InputStream: opts.InputStream,
   165  	})
   166  	if err != nil {
   167  		return fmt.Errorf("Error uploading input to container: %s", err)
   168  	}
   169  
   170  	//-----------------------------------------------------------------------------------
   171  	// Attach stdout buffer to capture possible compilation errors
   172  	//-----------------------------------------------------------------------------------
   173  	stdout := bytes.NewBuffer(nil)
   174  	_, err = client.AttachToContainerNonBlocking(docker.AttachToContainerOptions{
   175  		Container:    container.ID,
   176  		OutputStream: stdout,
   177  		ErrorStream:  stdout,
   178  		Logs:         true,
   179  		Stdout:       true,
   180  		Stderr:       true,
   181  		Stream:       true,
   182  	})
   183  	if err != nil {
   184  		return fmt.Errorf("Error attaching to container: %s", err)
   185  	}
   186  
   187  	//-----------------------------------------------------------------------------------
   188  	// Launch the actual build, realizing the Env/Cmd specified at container creation
   189  	//-----------------------------------------------------------------------------------
   190  	err = client.StartContainer(container.ID, nil)
   191  	if err != nil {
   192  		return fmt.Errorf("Error executing build: %s \"%s\"", err, stdout.String())
   193  	}
   194  
   195  	//-----------------------------------------------------------------------------------
   196  	// Wait for the build to complete and gather the return value
   197  	//-----------------------------------------------------------------------------------
   198  	retval, err := client.WaitContainer(container.ID)
   199  	if err != nil {
   200  		return fmt.Errorf("Error waiting for container to complete: %s", err)
   201  	}
   202  	if retval > 0 {
   203  		return fmt.Errorf("Error returned from build: %d \"%s\"", retval, stdout.String())
   204  	}
   205  
   206  	//-----------------------------------------------------------------------------------
   207  	// Finally, download the result
   208  	//-----------------------------------------------------------------------------------
   209  	err = client.DownloadFromContainer(container.ID, docker.DownloadFromContainerOptions{
   210  		Path:         "/chaincode/output/.",
   211  		OutputStream: opts.OutputStream,
   212  	})
   213  	if err != nil {
   214  		return fmt.Errorf("Error downloading output: %s", err)
   215  	}
   216  
   217  	return nil
   218  }