github.com/oskarth/go-ethereum@v1.6.8-0.20191013093314-dac24a9d3494/cmd/swarm/swarm-smoke/upload_and_sync.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of go-ethereum. 3 // 4 // go-ethereum is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // go-ethereum is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 16 17 package main 18 19 import ( 20 "bytes" 21 "crypto/md5" 22 crand "crypto/rand" 23 "errors" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "net/http" 28 "os" 29 "os/exec" 30 "strings" 31 "sync" 32 "time" 33 34 "github.com/ethereum/go-ethereum/log" 35 "github.com/pborman/uuid" 36 37 cli "gopkg.in/urfave/cli.v1" 38 ) 39 40 func generateEndpoints(scheme string, cluster string, from int, to int) { 41 if cluster == "prod" { 42 cluster = "" 43 } else if cluster == "local" { 44 for port := from; port <= to; port++ { 45 endpoints = append(endpoints, fmt.Sprintf("%s://localhost:%v", scheme, port)) 46 } 47 return 48 } else { 49 cluster = cluster + "." 50 } 51 52 for port := from; port <= to; port++ { 53 endpoints = append(endpoints, fmt.Sprintf("%s://%v.%sswarm-gateways.net", scheme, port, cluster)) 54 } 55 56 if includeLocalhost { 57 endpoints = append(endpoints, "http://localhost:8500") 58 } 59 } 60 61 func cliUploadAndSync(c *cli.Context) error { 62 defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now()) 63 64 generateEndpoints(scheme, cluster, from, to) 65 66 log.Info("uploading to " + endpoints[0] + " and syncing") 67 68 f, cleanup := generateRandomFile(filesize * 1000) 69 defer cleanup() 70 71 hash, err := upload(f, endpoints[0]) 72 if err != nil { 73 log.Error(err.Error()) 74 return err 75 } 76 77 fhash, err := digest(f) 78 if err != nil { 79 log.Error(err.Error()) 80 return err 81 } 82 83 log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash)) 84 85 time.Sleep(3 * time.Second) 86 87 wg := sync.WaitGroup{} 88 for _, endpoint := range endpoints { 89 ruid := uuid.New()[:8] 90 wg.Add(1) 91 go func(endpoint string, ruid string) { 92 for { 93 err := fetch(hash, endpoint, fhash, ruid) 94 if err != nil { 95 continue 96 } 97 98 wg.Done() 99 return 100 } 101 }(endpoint, ruid) 102 } 103 wg.Wait() 104 log.Info("all endpoints synced random file successfully") 105 106 return nil 107 } 108 109 // fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file 110 func fetch(hash string, endpoint string, original []byte, ruid string) error { 111 log.Trace("sleeping", "ruid", ruid) 112 time.Sleep(3 * time.Second) 113 114 log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash) 115 res, err := http.Get(endpoint + "/bzz:/" + hash + "/") 116 if err != nil { 117 log.Warn(err.Error(), "ruid", ruid) 118 return err 119 } 120 log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength) 121 122 if res.StatusCode != 200 { 123 err := fmt.Errorf("expected status code %d, got %v", 200, res.StatusCode) 124 log.Warn(err.Error(), "ruid", ruid) 125 return err 126 } 127 128 defer res.Body.Close() 129 130 rdigest, err := digest(res.Body) 131 if err != nil { 132 log.Warn(err.Error(), "ruid", ruid) 133 return err 134 } 135 136 if !bytes.Equal(rdigest, original) { 137 err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original) 138 log.Warn(err.Error(), "ruid", ruid) 139 return err 140 } 141 142 log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength) 143 144 return nil 145 } 146 147 // upload is uploading a file `f` to `endpoint` via the `swarm up` cmd 148 func upload(f *os.File, endpoint string) (string, error) { 149 var out bytes.Buffer 150 cmd := exec.Command("swarm", "--bzzapi", endpoint, "up", f.Name()) 151 cmd.Stdout = &out 152 err := cmd.Run() 153 if err != nil { 154 return "", err 155 } 156 hash := strings.TrimRight(out.String(), "\r\n") 157 return hash, nil 158 } 159 160 func digest(r io.Reader) ([]byte, error) { 161 h := md5.New() 162 _, err := io.Copy(h, r) 163 if err != nil { 164 return nil, err 165 } 166 return h.Sum(nil), nil 167 } 168 169 // generates random data in heap buffer 170 func generateRandomData(datasize int) ([]byte, error) { 171 b := make([]byte, datasize) 172 c, err := crand.Read(b) 173 if err != nil { 174 return nil, err 175 } else if c != datasize { 176 return nil, errors.New("short read") 177 } 178 return b, nil 179 } 180 181 // generateRandomFile is creating a temporary file with the requested byte size 182 func generateRandomFile(size int) (f *os.File, teardown func()) { 183 // create a tmp file 184 tmp, err := ioutil.TempFile("", "swarm-test") 185 if err != nil { 186 panic(err) 187 } 188 189 // callback for tmp file cleanup 190 teardown = func() { 191 tmp.Close() 192 os.Remove(tmp.Name()) 193 } 194 195 buf := make([]byte, size) 196 _, err = crand.Read(buf) 197 if err != nil { 198 panic(err) 199 } 200 ioutil.WriteFile(tmp.Name(), buf, 0755) 201 202 return tmp, teardown 203 }