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 }