github.com/shirikatsu/go-ethereum@v1.8.19/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 "crypto/tls" 24 "errors" 25 "fmt" 26 "io" 27 "io/ioutil" 28 "net/http" 29 "os" 30 "os/exec" 31 "strings" 32 "sync" 33 "time" 34 35 "github.com/ethereum/go-ethereum/log" 36 colorable "github.com/mattn/go-colorable" 37 "github.com/pborman/uuid" 38 39 cli "gopkg.in/urfave/cli.v1" 40 ) 41 42 func generateEndpoints(scheme string, cluster string, from int, to int) { 43 if cluster == "prod" { 44 for port := from; port <= to; port++ { 45 endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port)) 46 } 47 } else { 48 for port := from; port <= to; port++ { 49 endpoints = append(endpoints, fmt.Sprintf("%s://swarm-%v-%s.stg.swarm-gateways.net", scheme, port, cluster)) 50 } 51 } 52 53 if includeLocalhost { 54 endpoints = append(endpoints, "http://localhost:8500") 55 } 56 } 57 58 func cliUploadAndSync(c *cli.Context) error { 59 log.PrintOrigins(true) 60 log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) 61 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 client := &http.Client{Transport: &http.Transport{ 116 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 117 }} 118 res, err := client.Get(endpoint + "/bzz:/" + hash + "/") 119 if err != nil { 120 log.Warn(err.Error(), "ruid", ruid) 121 return err 122 } 123 log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength) 124 125 if res.StatusCode != 200 { 126 err := fmt.Errorf("expected status code %d, got %v", 200, res.StatusCode) 127 log.Warn(err.Error(), "ruid", ruid) 128 return err 129 } 130 131 defer res.Body.Close() 132 133 rdigest, err := digest(res.Body) 134 if err != nil { 135 log.Warn(err.Error(), "ruid", ruid) 136 return err 137 } 138 139 if !bytes.Equal(rdigest, original) { 140 err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original) 141 log.Warn(err.Error(), "ruid", ruid) 142 return err 143 } 144 145 log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength) 146 147 return nil 148 } 149 150 // upload is uploading a file `f` to `endpoint` via the `swarm up` cmd 151 func upload(f *os.File, endpoint string) (string, error) { 152 var out bytes.Buffer 153 cmd := exec.Command("swarm", "--bzzapi", endpoint, "up", f.Name()) 154 cmd.Stdout = &out 155 err := cmd.Run() 156 if err != nil { 157 return "", err 158 } 159 hash := strings.TrimRight(out.String(), "\r\n") 160 return hash, nil 161 } 162 163 func digest(r io.Reader) ([]byte, error) { 164 h := md5.New() 165 _, err := io.Copy(h, r) 166 if err != nil { 167 return nil, err 168 } 169 return h.Sum(nil), nil 170 } 171 172 // generates random data in heap buffer 173 func generateRandomData(datasize int) ([]byte, error) { 174 b := make([]byte, datasize) 175 c, err := crand.Read(b) 176 if err != nil { 177 return nil, err 178 } else if c != datasize { 179 return nil, errors.New("short read") 180 } 181 return b, nil 182 } 183 184 // generateRandomFile is creating a temporary file with the requested byte size 185 func generateRandomFile(size int) (f *os.File, teardown func()) { 186 // create a tmp file 187 tmp, err := ioutil.TempFile("", "swarm-test") 188 if err != nil { 189 panic(err) 190 } 191 192 // callback for tmp file cleanup 193 teardown = func() { 194 tmp.Close() 195 os.Remove(tmp.Name()) 196 } 197 198 buf := make([]byte, size) 199 _, err = crand.Read(buf) 200 if err != nil { 201 panic(err) 202 } 203 ioutil.WriteFile(tmp.Name(), buf, 0755) 204 205 return tmp, teardown 206 }