github.com/letterj/go-ethereum@v1.8.22-0.20190204142846-520024dfd689/cmd/swarm/swarm-smoke/util.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  	"context"
    22  	"crypto/md5"
    23  	crand "crypto/rand"
    24  	"errors"
    25  	"fmt"
    26  	"io"
    27  	"io/ioutil"
    28  	"net/http"
    29  	"net/http/httptrace"
    30  	"os"
    31  	"time"
    32  
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/ethereum/go-ethereum/metrics"
    35  	"github.com/ethereum/go-ethereum/swarm/api"
    36  	"github.com/ethereum/go-ethereum/swarm/api/client"
    37  	"github.com/ethereum/go-ethereum/swarm/spancontext"
    38  	opentracing "github.com/opentracing/opentracing-go"
    39  	cli "gopkg.in/urfave/cli.v1"
    40  )
    41  
    42  var (
    43  	commandName = ""
    44  )
    45  
    46  func wrapCliCommand(name string, killOnTimeout bool, command func(*cli.Context) error) func(*cli.Context) error {
    47  	return func(ctx *cli.Context) error {
    48  		log.PrintOrigins(true)
    49  		log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(false))))
    50  
    51  		defer func(now time.Time) {
    52  			totalTime := time.Since(now)
    53  			log.Info("total time", "time", totalTime, "kb", filesize)
    54  			metrics.GetOrRegisterResettingTimer(name+".total-time", nil).Update(totalTime)
    55  		}(time.Now())
    56  
    57  		log.Info("smoke test starting", "task", name, "timeout", timeout)
    58  		commandName = name
    59  		metrics.GetOrRegisterCounter(name, nil).Inc(1)
    60  
    61  		errc := make(chan error)
    62  		done := make(chan struct{})
    63  
    64  		if killOnTimeout {
    65  			go func() {
    66  				<-time.After(time.Duration(timeout) * time.Second)
    67  				close(done)
    68  			}()
    69  		}
    70  
    71  		go func() {
    72  			errc <- command(ctx)
    73  		}()
    74  
    75  		select {
    76  		case err := <-errc:
    77  			if err != nil {
    78  				metrics.GetOrRegisterCounter(fmt.Sprintf("%s.fail", name), nil).Inc(1)
    79  			}
    80  			return err
    81  		case <-done:
    82  			metrics.GetOrRegisterCounter(fmt.Sprintf("%s.timeout", name), nil).Inc(1)
    83  			return fmt.Errorf("timeout after %v sec", timeout)
    84  		}
    85  	}
    86  }
    87  
    88  func generateEndpoints(scheme string, cluster string, app string, from int, to int) {
    89  	if cluster == "prod" {
    90  		for port := from; port < to; port++ {
    91  			endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port))
    92  		}
    93  	} else if cluster == "private-internal" {
    94  		for port := from; port < to; port++ {
    95  			endpoints = append(endpoints, fmt.Sprintf("%s://swarm-private-internal-%v:8500", scheme, port))
    96  		}
    97  	} else {
    98  		for port := from; port < to; port++ {
    99  			endpoints = append(endpoints, fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, port, cluster))
   100  		}
   101  	}
   102  
   103  	if includeLocalhost {
   104  		endpoints = append(endpoints, "http://localhost:8500")
   105  	}
   106  }
   107  
   108  //just use the first endpoint
   109  func generateEndpoint(scheme string, cluster string, app string, from int) string {
   110  	if cluster == "prod" {
   111  		return fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, from)
   112  	} else if cluster == "private-internal" {
   113  		return fmt.Sprintf("%s://swarm-private-internal-%v:8500", scheme, from)
   114  	} else {
   115  		return fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, from, cluster)
   116  	}
   117  }
   118  
   119  func fetchFeed(topic string, user string, endpoint string, original []byte, ruid string) error {
   120  	ctx, sp := spancontext.StartSpan(context.Background(), "feed-and-sync.fetch")
   121  	defer sp.Finish()
   122  
   123  	log.Trace("sleeping", "ruid", ruid)
   124  	time.Sleep(3 * time.Second)
   125  
   126  	log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user)
   127  
   128  	var tn time.Time
   129  	reqUri := endpoint + "/bzz-feed:/?topic=" + topic + "&user=" + user
   130  	req, _ := http.NewRequest("GET", reqUri, nil)
   131  
   132  	opentracing.GlobalTracer().Inject(
   133  		sp.Context(),
   134  		opentracing.HTTPHeaders,
   135  		opentracing.HTTPHeadersCarrier(req.Header))
   136  
   137  	trace := client.GetClientTrace("feed-and-sync - http get", "feed-and-sync", ruid, &tn)
   138  
   139  	req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
   140  	transport := http.DefaultTransport
   141  
   142  	//transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
   143  
   144  	tn = time.Now()
   145  	res, err := transport.RoundTrip(req)
   146  	if err != nil {
   147  		log.Error(err.Error(), "ruid", ruid)
   148  		return err
   149  	}
   150  
   151  	log.Trace("http get response (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user, "code", res.StatusCode, "len", res.ContentLength)
   152  
   153  	if res.StatusCode != 200 {
   154  		return fmt.Errorf("expected status code %d, got %v (ruid %v)", 200, res.StatusCode, ruid)
   155  	}
   156  
   157  	defer res.Body.Close()
   158  
   159  	rdigest, err := digest(res.Body)
   160  	if err != nil {
   161  		log.Warn(err.Error(), "ruid", ruid)
   162  		return err
   163  	}
   164  
   165  	if !bytes.Equal(rdigest, original) {
   166  		err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original)
   167  		log.Warn(err.Error(), "ruid", ruid)
   168  		return err
   169  	}
   170  
   171  	log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength)
   172  
   173  	return nil
   174  }
   175  
   176  // fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file
   177  func fetch(hash string, endpoint string, original []byte, ruid string) error {
   178  	ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
   179  	defer sp.Finish()
   180  
   181  	log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
   182  
   183  	var tn time.Time
   184  	reqUri := endpoint + "/bzz:/" + hash + "/"
   185  	req, _ := http.NewRequest("GET", reqUri, nil)
   186  
   187  	opentracing.GlobalTracer().Inject(
   188  		sp.Context(),
   189  		opentracing.HTTPHeaders,
   190  		opentracing.HTTPHeadersCarrier(req.Header))
   191  
   192  	trace := client.GetClientTrace(commandName+" - http get", commandName, ruid, &tn)
   193  
   194  	req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
   195  	transport := http.DefaultTransport
   196  
   197  	//transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
   198  
   199  	tn = time.Now()
   200  	res, err := transport.RoundTrip(req)
   201  	if err != nil {
   202  		log.Error(err.Error(), "ruid", ruid)
   203  		return err
   204  	}
   205  	log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength)
   206  
   207  	if res.StatusCode != 200 {
   208  		err := fmt.Errorf("expected status code %d, got %v", 200, res.StatusCode)
   209  		log.Warn(err.Error(), "ruid", ruid)
   210  		return err
   211  	}
   212  
   213  	defer res.Body.Close()
   214  
   215  	rdigest, err := digest(res.Body)
   216  	if err != nil {
   217  		log.Warn(err.Error(), "ruid", ruid)
   218  		return err
   219  	}
   220  
   221  	if !bytes.Equal(rdigest, original) {
   222  		err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original)
   223  		log.Warn(err.Error(), "ruid", ruid)
   224  		return err
   225  	}
   226  
   227  	log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength)
   228  
   229  	return nil
   230  }
   231  
   232  // upload an arbitrary byte as a plaintext file  to `endpoint` using the api client
   233  func upload(r io.Reader, size int, endpoint string) (string, error) {
   234  	swarm := client.NewClient(endpoint)
   235  	f := &client.File{
   236  		ReadCloser: ioutil.NopCloser(r),
   237  		ManifestEntry: api.ManifestEntry{
   238  			ContentType: "text/plain",
   239  			Mode:        0660,
   240  			Size:        int64(size),
   241  		},
   242  	}
   243  
   244  	// upload data to bzz:// and retrieve the content-addressed manifest hash, hex-encoded.
   245  	return swarm.Upload(f, "", false)
   246  }
   247  
   248  func digest(r io.Reader) ([]byte, error) {
   249  	h := md5.New()
   250  	_, err := io.Copy(h, r)
   251  	if err != nil {
   252  		return nil, err
   253  	}
   254  	return h.Sum(nil), nil
   255  }
   256  
   257  // generates random data in heap buffer
   258  func generateRandomData(datasize int) ([]byte, error) {
   259  	b := make([]byte, datasize)
   260  	c, err := crand.Read(b)
   261  	if err != nil {
   262  		return nil, err
   263  	} else if c != datasize {
   264  		return nil, errors.New("short read")
   265  	}
   266  	return b, nil
   267  }