diff --git a/args_parser.go b/args_parser.go index 0995440..ce6b258 100644 --- a/args_parser.go +++ b/args_parser.go @@ -37,6 +37,8 @@ type kingpinParser struct { printSpec *nullableString noPrint bool + + formatSpec string } func newKingpinParser() argsParser { @@ -59,6 +61,7 @@ func newKingpinParser() argsParser { clientType: fhttp, printSpec: new(nullableString), noPrint: false, + formatSpec: "plain-text", } app := kingpin.New("", "Fast cross-platform HTTP benchmarking tool"). @@ -155,6 +158,19 @@ func newKingpinParser() argsParser { Short('q'). BoolVar(&kparser.noPrint) + app.Flag("format", "Which format to use to output the result. "+ + " is either a name (or its shorthand) of some format "+ + "understood by bombardier or a path to the user-defined template, "+ + "which uses Go's text/template syntax, prefixed with 'path:' string "+ + "(without single quotes), i.e. \"path:/some/path/to/your.template\" "+ + " or \"path:C:\\some\\path\\to\\your.template\" in case of Windows. "+ + "Formats understood by bombardier are:"+ + "\n\t* plain-text (short: pt)"+ + "\n\t* json (short: j)"). + PlaceHolder(""). + Short('o'). + StringVar(&kparser.formatSpec) + app.Arg("url", "Target's URL").Required(). StringVar(&kparser.url) @@ -178,6 +194,12 @@ func (k *kingpinParser) parse(args []string) (config, error) { if k.noPrint { pi, pp, pr = false, false, false } + format := formatFromString(k.formatSpec) + if format == nil { + return emptyConf, fmt.Errorf( + "unknown format or invalid format spec %q", k.formatSpec, + ) + } return config{ numConns: k.numConns, numReqs: k.numReqs.val, @@ -198,6 +220,7 @@ func (k *kingpinParser) parse(args []string) (config, error) { printIntro: pi, printProgress: pp, printResult: pr, + format: format, }, nil } diff --git a/args_parser_test.go b/args_parser_test.go index 01ae1e2..ec01fcc 100644 --- a/args_parser_test.go +++ b/args_parser_test.go @@ -61,6 +61,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -104,6 +105,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -129,6 +131,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -154,6 +157,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -182,6 +186,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -221,6 +226,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -256,6 +262,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -291,6 +298,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -315,6 +323,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -335,6 +344,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -355,6 +365,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -385,6 +396,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -410,6 +422,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -428,6 +441,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -472,6 +486,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -516,6 +531,7 @@ func TestArgsParsing(t *testing.T) { printIntro: true, printProgress: false, printResult: true, + format: knownFormat("plain-text"), }, }, { @@ -540,6 +556,127 @@ func TestArgsParsing(t *testing.T) { printIntro: false, printProgress: false, printResult: false, + format: knownFormat("plain-text"), + }, + }, + { + [][]string{ + { + programName, + "--format", "plain-text", + "https://somehost.somedomain", + }, + { + programName, + "--format", "pt", + "https://somehost.somedomain", + }, + { + programName, + "--format=plain-text", + "https://somehost.somedomain", + }, + { + programName, + "--format=pt", + "https://somehost.somedomain", + }, + { + programName, + "-o", "plain-text", + "https://somehost.somedomain", + }, + { + programName, + "-o", "pt", + "https://somehost.somedomain", + }, + }, + config{ + numConns: defaultNumberOfConns, + timeout: defaultTimeout, + headers: new(headersList), + method: "GET", + url: "https://somehost.somedomain", + printIntro: true, + printProgress: true, + printResult: true, + format: knownFormat("plain-text"), + }, + }, + { + [][]string{ + { + programName, + "--format", "json", + "https://somehost.somedomain", + }, + { + programName, + "--format", "j", + "https://somehost.somedomain", + }, + { + programName, + "--format=json", + "https://somehost.somedomain", + }, + { + programName, + "--format=j", + "https://somehost.somedomain", + }, + { + programName, + "-o", "json", + "https://somehost.somedomain", + }, + { + programName, + "-o", "j", + "https://somehost.somedomain", + }, + }, + config{ + numConns: defaultNumberOfConns, + timeout: defaultTimeout, + headers: new(headersList), + method: "GET", + url: "https://somehost.somedomain", + printIntro: true, + printProgress: true, + printResult: true, + format: knownFormat("json"), + }, + }, + { + [][]string{ + { + programName, + "--format", "path:/path/to/tmpl.txt", + "https://somehost.somedomain", + }, + { + programName, + "--format=path:/path/to/tmpl.txt", + "https://somehost.somedomain", + }, + { + programName, + "-o", "path:/path/to/tmpl.txt", + "https://somehost.somedomain", + }, + }, + config{ + numConns: defaultNumberOfConns, + timeout: defaultTimeout, + headers: new(headersList), + method: "GET", + url: "https://somehost.somedomain", + printIntro: true, + printProgress: true, + printResult: true, + format: userDefinedTemplate("/path/to/tmpl.txt"), }, }, } @@ -635,3 +772,21 @@ func TestArgsParsingWithEmptyPrintSpec(t *testing.T) { t.Fail() } } + +func TestArgsParsingWithInvalidPrintSpec(t *testing.T) { + invalidSpecs := [][]string{ + {programName, "--format", "noprefix.txt", "somehost.somedomain"}, + {programName, "--format=noprefix.txt", "somehost.somedomain"}, + {programName, "-o", "noprefix.txt", "somehost.somedomain"}, + {programName, "--format", "unknown-format", "somehost.somedomain"}, + {programName, "--format=unknown-format", "somehost.somedomain"}, + {programName, "-o", "unknown-format", "somehost.somedomain"}, + } + p := newKingpinParser() + for _, is := range invalidSpecs { + c, err := p.parse(is) + if err == nil || c != emptyConf { + t.Errorf("invalid print spec %q parsed correctly", is) + } + } +} diff --git a/bombardier.go b/bombardier.go index c91ac99..a0b4fbf 100644 --- a/bombardier.go +++ b/bombardier.go @@ -17,6 +17,7 @@ import ( "github.com/cheggaaa/pb" fhist "github.com/codesenberg/concurrent/float64/histogram" uhist "github.com/codesenberg/concurrent/uint64/histogram" + "github.com/satori/go.uuid" ) type bombardier struct { @@ -54,7 +55,8 @@ type bombardier struct { bar *pb.ProgressBar // Output - out io.Writer + out io.Writer + template *template.Template } func newBombardier(c config) (*bombardier, error) { @@ -113,11 +115,12 @@ func newBombardier(c config) (*bombardier, error) { } else { pbody = &c.body if c.bodyFilePath != "" { - body, err := ioutil.ReadFile(c.bodyFilePath) + var bodyBytes []byte + bodyBytes, err = ioutil.ReadFile(c.bodyFilePath) if err != nil { return nil, err } - sbody := string(body) + sbody := string(bodyBytes) pbody = &sbody } } @@ -143,6 +146,11 @@ func newBombardier(c config) (*bombardier, error) { b.bar.NotPrint = true } + b.template, err = b.prepareTemplate() + if err != nil { + return nil, err + } + b.workers.Add(int(c.numConns)) b.errors = newErrorMap() b.doneChan = make(chan struct{}, 2) @@ -165,6 +173,54 @@ func makeHTTPClient(clientType clientTyp, cc *clientOpts) client { return cl } +func (b *bombardier) prepareTemplate() (*template.Template, error) { + var ( + templateBytes []byte + err error + ) + switch f := b.conf.format.(type) { + case knownFormat: + templateBytes = f.template() + case userDefinedTemplate: + templateBytes, err = ioutil.ReadFile(string(f)) + if err != nil { + return nil, err + } + default: + panic("format can't be nil at this point, this is a bug") + } + outputTemplate, err := template.New("output-template"). + Funcs(template.FuncMap{ + "WithLatencies": func() bool { + return b.conf.printLatencies + }, + "FormatBinary": formatBinary, + "FormatTimeUs": formatTimeUs, + "FormatTimeUsUint64": func(us uint64) string { + return formatTimeUs(float64(us)) + }, + "FloatsToArray": func(ps ...float64) []float64 { + return ps + }, + "Multiply": func(num, coeff float64) float64 { + return num * coeff + }, + "StringToBytes": func(s string) []byte { + return []byte(s) + }, + "UUIDV1": uuid.NewV1, + "UUIDV2": uuid.NewV2, + "UUIDV3": uuid.NewV3, + "UUIDV4": uuid.NewV4, + "UUIDV5": uuid.NewV5, + }).Parse(string(templateBytes)) + + if err != nil { + return nil, err + } + return outputTemplate, nil +} + func (b *bombardier) writeStatistics( code int, msTaken uint64, ) { @@ -367,61 +423,12 @@ func (b *bombardier) gatherInfo() internal.TestInfo { func (b *bombardier) printStats() { info := b.gatherInfo() - tmpl := newPlainTextTemplate(b.conf.printLatencies) - err := tmpl.Execute(b.out, info) + err := b.template.Execute(b.out, info) if err != nil { fmt.Fprintln(os.Stderr, err) } } -func newPlainTextTemplate(printLatencies bool) *template.Template { - return template.Must(template.New("plain-text").Funcs(template.FuncMap{ - "WithLatencies": func() bool { - return printLatencies - }, - "FormatBinary": formatBinary, - "FormatTimeUs": formatTimeUs, - "FormatTimeUsUint64": func(us uint64) string { - return formatTimeUs(float64(us)) - }, - "Percentiles": func() []float64 { - return []float64{0.5, 0.75, 0.9, 0.99} - }, - "Multiply": func(num, coeff float64) float64 { - return num * coeff - }, - }).Parse(` -{{- printf "%10v %10v %10v %10v" "Statistics" "Avg" "Stdev" "Max" }} -{{ with .Result.RequestsStats Percentiles }} - {{- printf " %-10v %10.2f %10.2f %10.2f" "Reqs/sec" .Mean .Stddev .Max -}} -{{ else }} - {{- print " There wasn't enough data to compute statistics for requests." }} -{{ end }} -{{ with .Result.LatenciesStats Percentiles }} - {{- printf " %-10v %10v %10v %10v" "Latency" (FormatTimeUs .Mean) (FormatTimeUs .Stddev) (FormatTimeUs .Max) }} - {{- if WithLatencies }} - {{- "\n Latency Distribution" }} - {{- range $pc, $lat := .Percentiles }} - {{- printf "\n %2.0f%% %10s" (Multiply $pc 100) (FormatTimeUsUint64 $lat) -}} - {{ end -}} - {{ end }} -{{ else }} - {{- print " There wasn't enough data to compute statistics for latencies." }} -{{ end -}} -{{ with .Result -}} -{{ " HTTP codes:" }} -{{ printf " 1xx - %v, 2xx - %v, 3xx - %v, 4xx - %v, 5xx - %v" .Req1XX .Req2XX .Req3XX .Req4XX .Req5XX }} - {{- printf "\n others - %v" .Others }} - {{- with .Errors }} - {{- "\n Errors:"}} - {{- range . }} - {{- printf "\n %10v - %v" .Error .Count }} - {{- end -}} - {{ end -}} -{{ end }} -{{ printf " %-10v %10v/s" "Throughput:" (FormatBinary .Result.Throughput)}}`)) -} - func (b *bombardier) redirectOutputTo(out io.Writer) { b.bar.Output = out b.out = out diff --git a/bombardier_test.go b/bombardier_test.go index 147578e..c627232 100644 --- a/bombardier_test.go +++ b/bombardier_test.go @@ -45,6 +45,7 @@ func testBombardierShouldFireSpecifiedNumberOfRequests( method: "GET", body: "", clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -79,6 +80,7 @@ func testBombardierShouldFinish(clientType clientTyp, t *testing.T) { method: "GET", body: "", clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -141,6 +143,7 @@ func testBombardierShouldSendHeaders(clientType clientTyp, t *testing.T) { method: "GET", body: "", clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -186,6 +189,7 @@ func testBombardierHTTPCodeRecording(clientType clientTyp, t *testing.T) { method: "GET", body: "", clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -235,6 +239,7 @@ func testBombardierTimeoutRecoding(clientType clientTyp, t *testing.T) { method: "GET", body: "", clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -272,6 +277,7 @@ func testBombardierThroughputRecording(clientType clientTyp, t *testing.T) { method: "GET", body: "", clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -308,6 +314,7 @@ func TestBombardierStatsPrinting(t *testing.T) { printIntro: true, printProgress: true, printResult: true, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -341,6 +348,7 @@ func TestBombardierErrorIfFailToReadClientCert(t *testing.T) { printLatencies: true, certPath: "certPath", keyPath: "keyPath", + format: knownFormat("plain-text"), }) if e == nil { t.Fail() @@ -412,6 +420,7 @@ func testBombardierClientCerts(clientType clientTyp, t *testing.T) { keyPath: "testclient.key", insecure: true, clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -461,6 +470,7 @@ func testBombardierRateLimiting(clientType clientTyp, t *testing.T) { body: "", rate: &rate, clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -517,6 +527,7 @@ func testBombardierSendsBody(clientType clientTyp, t *testing.T) { method: "POST", body: requestBody, clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -565,6 +576,7 @@ func testBombardierSendsBodyFromFile(clientType clientTyp, t *testing.T) { method: "POST", bodyFilePath: bodyPath, clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -583,6 +595,7 @@ func TestBombardierFileDoesntExist(t *testing.T) { timeout: defaultTimeout, method: "POST", bodyFilePath: bodyPath, + format: knownFormat("plain-text"), }) _, ok := e.(*os.PathError) if !ok { @@ -628,6 +641,7 @@ func testBombardierStreamsBody(clientType clientTyp, t *testing.T) { body: requestBody, stream: true, clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) @@ -680,6 +694,7 @@ func testBombardierStreamsBodyFromFile(clientType clientTyp, t *testing.T) { bodyFilePath: bodyPath, stream: true, clientType: clientType, + format: knownFormat("plain-text"), }) if e != nil { t.Error(e) diff --git a/config.go b/config.go index d30fe3a..0a652e9 100644 --- a/config.go +++ b/config.go @@ -17,11 +17,17 @@ type config struct { stream bool headers *headersList timeout time.Duration - printLatencies, insecure bool - rate *uint64 - clientType clientTyp + // TODO(codesenberg): printLatencies should probably be + // re(named&maked) into printPercentiles or even let + // users provide their own percentiles and not just + // calculate for [0.5, 0.75, 0.9, 0.99] + printLatencies, insecure bool + rate *uint64 + clientType clientTyp printIntro, printProgress, printResult bool + + format format } type testTyp int diff --git a/config_test.go b/config_test.go index b0dbcea..f1ad05e 100644 --- a/config_test.go +++ b/config_test.go @@ -69,6 +69,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", body: "", + format: knownFormat("plain-text"), }, errInvalidURL, }, @@ -82,6 +83,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", body: "", + format: knownFormat("plain-text"), }, errInvalidNumberOfConns, }, @@ -95,6 +97,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", body: "", + format: knownFormat("plain-text"), }, errInvalidNumberOfRequests, }, @@ -108,6 +111,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", body: "", + format: knownFormat("plain-text"), }, errInvalidTestDuration, }, @@ -121,6 +125,7 @@ func TestCheckArgs(t *testing.T) { timeout: negativeTimeoutDuration, method: "GET", body: "", + format: knownFormat("plain-text"), }, errNegativeTimeout, }, @@ -134,6 +139,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", body: "BODY", + format: knownFormat("plain-text"), }, errBodyNotAllowed, }, @@ -147,6 +153,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", bodyFilePath: "testbody.txt", + format: knownFormat("plain-text"), }, errBodyNotAllowed, }, @@ -160,6 +167,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", body: "", + format: knownFormat("plain-text"), }, nil, }, @@ -175,6 +183,7 @@ func TestCheckArgs(t *testing.T) { body: "", certPath: "test_cert.pem", keyPath: "", + format: knownFormat("plain-text"), }, errNoPathToKey, }, @@ -190,6 +199,7 @@ func TestCheckArgs(t *testing.T) { body: "", certPath: "", keyPath: "test_key.pem", + format: knownFormat("plain-text"), }, errNoPathToCert, }, @@ -203,6 +213,7 @@ func TestCheckArgs(t *testing.T) { timeout: defaultTimeout, method: "GET", rate: &zeroRate, + format: knownFormat("plain-text"), }, errZeroRate, }, @@ -217,6 +228,7 @@ func TestCheckArgs(t *testing.T) { method: "POST", body: "abracadabra", bodyFilePath: "testbody.txt", + format: knownFormat("plain-text"), }, errBodyProvidedTwice, }, diff --git a/doc.go b/doc.go index 6972980..0716512 100644 --- a/doc.go +++ b/doc.go @@ -40,8 +40,25 @@ Flags: * r (result only) * result (same as above) -q, --no-print Don't output anything + -o, --format= Which format to use to output the result. + is either a name (or its shorthand) of some format + understood by bombardier or a path to the + user-defined template, which uses Go's + text/template syntax, prefixed with 'path:' string + (without single quotes), i.e. + "path:/some/path/to/your.template" or + "path:C:\some\path\to\your.template" in case of + Windows. Formats understood by bombardier are: + + * plain-text (short: pt) + * json (short: j) Args: Target's URL + +For detailed documentation on user-defined templates see +documentation for package github.com/codesenberg/bombardier/template. +Link (GoDoc): +https://godoc.org/github.com/codesenberg/bombardier/template */ package main diff --git a/internal/attack_result.go b/internal/test_info.go similarity index 100% rename from internal/attack_result.go rename to internal/test_info.go diff --git a/template/doc.go b/template/doc.go new file mode 100644 index 0000000..06d4127 --- /dev/null +++ b/template/doc.go @@ -0,0 +1,54 @@ +/* +Package template documents the way user-defined output templates are +ment to be used. + +User-defined templates use Go's text/template package, so you might +want to check its documentation first. +There are a bunch of helper methods available inside a template +besides those described in aforementioned documentation, namely: + - WithLatencies() + Tells whether --latencies flag were activated. + - FormatBinary(numberOfBytes float64) string + Converts bytes to kilo-, mega-, giga-, etc.- bytes, and + appends appropriate suffix "KB", "MB", "GB", etc. + - FormatTimeUs(us float64) string + Converts microseconds to milliseconds, seconds, minutes or + hours and appends appropriate suffix. + - FormatTimeUsUint64(us uint64) string + Same as above, but for uint64, since type conversions are + not available in templates. + - FloatsToArray(ps ...float64) []float64 + Converts a bunch of floats into array, since, again, + type conversions are not available in templates. + - Multiply(num, coeff float64) float64 + Arithmetics are not available inside of templates either. + - StringToBytes(s string) []byte + Convenience function to convert string to []byte. + - UUIDV1() (UUID, error) + Generates UUID Version 1, based on timestamp and + MAC address (RFC 4122) + - UUIDV2(domain byte) (UUID, error) + Generates UUID Version 2, based on timestamp, MAC address + and POSIX UID/GID (DCE 1.1) + - UUIDV3(ns UUID, name string) UUID + Generates UUID Version 3, based on MD5 hashing (RFC 4122) + - UUIDV4() (UUID, error) + Generates UUID Version 4, based on random numbers (RFC 4122) + - UUIDV5(ns UUID, name string) UUID + Generates UUID Version 5, based on SHA-1 hashing (RFC 4122) + +The structure that gets passed to the template is documented in +the package github.com/codesenberg/bombardier/internal. The structure +of interest is TestInfo. It basically consists of Spec and Result +fields, the former contains various information about the test +(number of connections, URL, HTTP method, headers, body, rate, etc.) +performed, while the latter contains results obtained during the +execution of this test (bytes read/written, time taken, RPS, etc.). + +Link to GoDoc for the structure used in template: +https://godoc.org/github.com/codesenberg/bombardier/internal#TestInfo + +Examples of templates can be found in: +https://github.com/codesenberg/bombardier/blob/master/template.go +*/ +package template diff --git a/templates.go b/templates.go new file mode 100644 index 0000000..e2a107f --- /dev/null +++ b/templates.go @@ -0,0 +1,171 @@ +package main + +import "strings" + +var ( + templates = map[string][]byte{ + "plain-text": []byte(plainTextTemplate), + "json": []byte(jsonTemplate), + } +) + +type format interface{} +type knownFormat string + +func (kf knownFormat) template() []byte { + return templates[string(kf)] +} + +type filePath string +type userDefinedTemplate filePath + +func formatFromString(formatSpec string) format { + const prefix = "path:" + if strings.HasPrefix(formatSpec, prefix) { + return userDefinedTemplate(formatSpec[len(prefix):]) + } + switch formatSpec { + case "pt", "plain-text": + return knownFormat("plain-text") + case "j", "json": + return knownFormat("json") + } + // nil represents unknown format + return nil +} + +const ( + plainTextTemplate = ` +{{- printf "%10v %10v %10v %10v" "Statistics" "Avg" "Stdev" "Max" }} +{{ with .Result.RequestsStats (FloatsToArray 0.5 0.75 0.9 0.99) }} + {{- printf " %-10v %10.2f %10.2f %10.2f" "Reqs/sec" .Mean .Stddev .Max -}} +{{ else }} + {{- print " There wasn't enough data to compute statistics for requests." }} +{{ end }} +{{ with .Result.LatenciesStats (FloatsToArray 0.5 0.75 0.9 0.99) }} + {{- printf " %-10v %10v %10v %10v" "Latency" (FormatTimeUs .Mean) (FormatTimeUs .Stddev) (FormatTimeUs .Max) }} + {{- if WithLatencies }} + {{- "\n Latency Distribution" }} + {{- range $pc, $lat := .Percentiles }} + {{- printf "\n %2.0f%% %10s" (Multiply $pc 100) (FormatTimeUsUint64 $lat) -}} + {{ end -}} + {{ end }} +{{ else }} + {{- print " There wasn't enough data to compute statistics for latencies." }} +{{ end -}} +{{ with .Result -}} +{{ " HTTP codes:" }} +{{ printf " 1xx - %v, 2xx - %v, 3xx - %v, 4xx - %v, 5xx - %v" .Req1XX .Req2XX .Req3XX .Req4XX .Req5XX }} + {{- printf "\n others - %v" .Others }} + {{- with .Errors }} + {{- "\n Errors:"}} + {{- range . }} + {{- printf "\n %10v - %v" .Error .Count }} + {{- end -}} + {{ end -}} +{{ end }} +{{ printf " %-10v %10v/s" "Throughput:" (FormatBinary .Result.Throughput)}}` + jsonTemplate = `{"spec":{ +{{- with .Spec -}} +"numberOfConnections":{{ .NumberOfConnections }} + +{{- if .IsTimedTest -}} +,"testType":"timed","testDurationSeconds":{{ .TestDuration.Seconds }} +{{- else -}} +,"testType":"number-of-requests","numberOfRequests":{{ .NumberOfRequests }} +{{- end -}} + +,"method":"{{ .Method }}","url":{{ .URL | printf "%q" }} + +{{- with .Headers -}} +,"headers":[ +{{- range $index, $header := . -}} +{{- if ne $index 0 -}},{{- end -}} +{"key":{{ .Key | printf "%q" }},"value":{{ .Value | printf "%q" }}} +{{- end -}} +] +{{- end -}} + +{{- if .BodyFilePath -}} +,"bodyFilePath":{{ .BodyFilePath | printf "%q" }} +{{- else -}} +,"body":{{ .Body | printf "%q" }} +{{- end -}} + +{{- if .CertPath -}} +,"certPath":{{ .CertPath | printf "%q" }} +{{- end -}} +{{- if .KeyPath -}} +,"keyPath":{{ .KeyPath | printf "%q" }} +{{- end -}} + +,"stream":{{ .Stream }},"timeoutSeconds":{{ .Timeout.Seconds }} + +{{- if .IsFastHTTP -}} +,"client":"fasthttp" +{{- end -}} +{{- if .IsNetHTTPV1 -}} +,"client":"net/http.v1" +{{- end -}} +{{- if .IsNetHTTPV2 -}} +,"client":"net/http.v2" +{{- end -}} + +{{- with .Rate -}} +,"rate":{{ . }} +{{- end -}} +{{- end -}} +}, + +{{- with .Result -}} +"result":{"bytesRead":{{ .BytesRead -}} +,"bytesWritten":{{ .BytesWritten -}} +,"timeTakenSeconds":{{ .TimeTaken.Seconds -}} + +,"req1xx":{{ .Req1XX -}} +,"req2xx":{{ .Req2XX -}} +,"req3xx":{{ .Req3XX -}} +,"req4xx":{{ .Req4XX -}} +,"req5xx":{{ .Req5XX -}} +,"others":{{ .Others -}} + +{{- with .Errors -}} +,"errors":[ +{{- range $index, $error := . -}} +{{- if ne $index 0 -}},{{- end -}} +{"description":{{ .Error | printf "%q" }},"count":{{ .Count }}} +{{- end -}} +] +{{- end -}} + +{{- with .LatenciesStats (FloatsToArray 0.5 0.75 0.9 0.99) -}} +,"latency":{"mean":{{ .Mean -}} +,"stddev":{{ .Stddev -}} +,"max":{{ .Max -}} + +{{- if WithLatencies -}} +,"percentiles":{ +{{- range $pc, $lat := .Percentiles }} +{{- if ne $pc 0.5 -}},{{- end -}} +{{- printf "\"%2.0f\":%d" (Multiply $pc 100) $lat -}} +{{- end -}} +} +{{- end -}} + +} +{{- end -}} + +{{- with .RequestsStats (FloatsToArray 0.5 0.75 0.9 0.99) -}} +,"rps":{"mean":{{ .Mean -}} +,"stddev":{{ .Stddev -}} +,"max":{{ .Max -}} +,"percentiles":{ +{{- range $pc, $rps := .Percentiles }} +{{- if ne $pc 0.5 -}},{{- end -}} +{{- printf "\"%2.0f\":%f" (Multiply $pc 100) $rps -}} +{{- end -}} +}} +{{- end -}} +}} +{{- end -}}` +) diff --git a/vendor/github.com/satori/go.uuid/LICENSE b/vendor/github.com/satori/go.uuid/LICENSE new file mode 100644 index 0000000..926d549 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/LICENSE @@ -0,0 +1,20 @@ +Copyright (C) 2013-2018 by Maxim Bublis + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/satori/go.uuid/README.md b/vendor/github.com/satori/go.uuid/README.md new file mode 100644 index 0000000..7702849 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/README.md @@ -0,0 +1,74 @@ +# UUID package for Go language + +[![Build Status](https://travis-ci.org/satori/go.uuid.svg?branch=master)](https://travis-ci.org/satori/go.uuid) +[![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid) +[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.svg)](http://godoc.org/github.com/satori/go.uuid) + +This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. + +With 100% test coverage and benchmarks out of box. + +Supported versions: +* Version 1, based on timestamp and MAC address (RFC 4122) +* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) +* Version 3, based on MD5 hashing (RFC 4122) +* Version 4, based on random numbers (RFC 4122) +* Version 5, based on SHA-1 hashing (RFC 4122) + +## Installation + +Use the `go` command: + + $ go get github.com/satori/go.uuid + +## Requirements + +UUID package requires Go >= 1.2. + +## Example + +```go +package main + +import ( + "fmt" + "github.com/satori/go.uuid" +) + +func main() { + // Creating UUID Version 4 + // panic on error + u1 := uuid.Must(uuid.NewV4()) + fmt.Printf("UUIDv4: %s\n", u1) + + // or error handling + u2, err := uuid.NewV4() + if err != nil { + fmt.Printf("Something went wrong: %s", err) + return + } + fmt.Printf("UUIDv4: %s\n", u2) + + // Parsing UUID from string input + u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") + if err != nil { + fmt.Printf("Something went wrong: %s", err) + } + fmt.Printf("Successfully parsed: %s", u2) +} +``` + +## Documentation + +[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. + +## Links +* [RFC 4122](http://tools.ietf.org/html/rfc4122) +* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) + +## Copyright + +Copyright (C) 2013-2018 by Maxim Bublis . + +UUID package released under MIT License. +See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. diff --git a/vendor/github.com/satori/go.uuid/codec.go b/vendor/github.com/satori/go.uuid/codec.go new file mode 100644 index 0000000..656892c --- /dev/null +++ b/vendor/github.com/satori/go.uuid/codec.go @@ -0,0 +1,206 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "bytes" + "encoding/hex" + "fmt" +) + +// FromBytes returns UUID converted from raw byte slice input. +// It will return error if the slice isn't 16 bytes long. +func FromBytes(input []byte) (u UUID, err error) { + err = u.UnmarshalBinary(input) + return +} + +// FromBytesOrNil returns UUID converted from raw byte slice input. +// Same behavior as FromBytes, but returns a Nil UUID on error. +func FromBytesOrNil(input []byte) UUID { + uuid, err := FromBytes(input) + if err != nil { + return Nil + } + return uuid +} + +// FromString returns UUID parsed from string input. +// Input is expected in a form accepted by UnmarshalText. +func FromString(input string) (u UUID, err error) { + err = u.UnmarshalText([]byte(input)) + return +} + +// FromStringOrNil returns UUID parsed from string input. +// Same behavior as FromString, but returns a Nil UUID on error. +func FromStringOrNil(input string) UUID { + uuid, err := FromString(input) + if err != nil { + return Nil + } + return uuid +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (u UUID) MarshalText() (text []byte, err error) { + text = []byte(u.String()) + return +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Following formats are supported: +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8", +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}", +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" +// "6ba7b8109dad11d180b400c04fd430c8" +// ABNF for supported UUID text representation follows: +// uuid := canonical | hashlike | braced | urn +// plain := canonical | hashlike +// canonical := 4hexoct '-' 2hexoct '-' 2hexoct '-' 6hexoct +// hashlike := 12hexoct +// braced := '{' plain '}' +// urn := URN ':' UUID-NID ':' plain +// URN := 'urn' +// UUID-NID := 'uuid' +// 12hexoct := 6hexoct 6hexoct +// 6hexoct := 4hexoct 2hexoct +// 4hexoct := 2hexoct 2hexoct +// 2hexoct := hexoct hexoct +// hexoct := hexdig hexdig +// hexdig := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | +// 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | +// 'A' | 'B' | 'C' | 'D' | 'E' | 'F' +func (u *UUID) UnmarshalText(text []byte) (err error) { + switch len(text) { + case 32: + return u.decodeHashLike(text) + case 36: + return u.decodeCanonical(text) + case 38: + return u.decodeBraced(text) + case 41: + fallthrough + case 45: + return u.decodeURN(text) + default: + return fmt.Errorf("uuid: incorrect UUID length: %s", text) + } +} + +// decodeCanonical decodes UUID string in format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8". +func (u *UUID) decodeCanonical(t []byte) (err error) { + if t[8] != '-' || t[13] != '-' || t[18] != '-' || t[23] != '-' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + src := t[:] + dst := u[:] + + for i, byteGroup := range byteGroups { + if i > 0 { + src = src[1:] // skip dash + } + _, err = hex.Decode(dst[:byteGroup/2], src[:byteGroup]) + if err != nil { + return + } + src = src[byteGroup:] + dst = dst[byteGroup/2:] + } + + return +} + +// decodeHashLike decodes UUID string in format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeHashLike(t []byte) (err error) { + src := t[:] + dst := u[:] + + if _, err = hex.Decode(dst, src); err != nil { + return err + } + return +} + +// decodeBraced decodes UUID string in format +// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}" or in format +// "{6ba7b8109dad11d180b400c04fd430c8}". +func (u *UUID) decodeBraced(t []byte) (err error) { + l := len(t) + + if t[0] != '{' || t[l-1] != '}' { + return fmt.Errorf("uuid: incorrect UUID format %s", t) + } + + return u.decodePlain(t[1 : l-1]) +} + +// decodeURN decodes UUID string in format +// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in format +// "urn:uuid:6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodeURN(t []byte) (err error) { + total := len(t) + + urn_uuid_prefix := t[:9] + + if !bytes.Equal(urn_uuid_prefix, urnPrefix) { + return fmt.Errorf("uuid: incorrect UUID format: %s", t) + } + + return u.decodePlain(t[9:total]) +} + +// decodePlain decodes UUID string in canonical format +// "6ba7b810-9dad-11d1-80b4-00c04fd430c8" or in hash-like format +// "6ba7b8109dad11d180b400c04fd430c8". +func (u *UUID) decodePlain(t []byte) (err error) { + switch len(t) { + case 32: + return u.decodeHashLike(t) + case 36: + return u.decodeCanonical(t) + default: + return fmt.Errorf("uuid: incorrrect UUID length: %s", t) + } +} + +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (u UUID) MarshalBinary() (data []byte, err error) { + data = u.Bytes() + return +} + +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +// It will return error if the slice isn't 16 bytes long. +func (u *UUID) UnmarshalBinary(data []byte) (err error) { + if len(data) != Size { + err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data)) + return + } + copy(u[:], data) + + return +} diff --git a/vendor/github.com/satori/go.uuid/generator.go b/vendor/github.com/satori/go.uuid/generator.go new file mode 100644 index 0000000..499dc35 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/generator.go @@ -0,0 +1,265 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "crypto/md5" + "crypto/rand" + "crypto/sha1" + "encoding/binary" + "fmt" + "hash" + "io" + "net" + "os" + "sync" + "time" +) + +// Difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970). +const epochStart = 122192928000000000 + +type epochFunc func() time.Time +type hwAddrFunc func() (net.HardwareAddr, error) + +var ( + global = newRFC4122Generator() + + posixUID = uint32(os.Getuid()) + posixGID = uint32(os.Getgid()) +) + +// NewV1 returns UUID based on current timestamp and MAC address. +func NewV1() (UUID, error) { + return global.NewV1() +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func NewV2(domain byte) (UUID, error) { + return global.NewV2(domain) +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func NewV3(ns UUID, name string) UUID { + return global.NewV3(ns, name) +} + +// NewV4 returns random generated UUID. +func NewV4() (UUID, error) { + return global.NewV4() +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func NewV5(ns UUID, name string) UUID { + return global.NewV5(ns, name) +} + +// Generator provides interface for generating UUIDs. +type Generator interface { + NewV1() (UUID, error) + NewV2(domain byte) (UUID, error) + NewV3(ns UUID, name string) UUID + NewV4() (UUID, error) + NewV5(ns UUID, name string) UUID +} + +// Default generator implementation. +type rfc4122Generator struct { + clockSequenceOnce sync.Once + hardwareAddrOnce sync.Once + storageMutex sync.Mutex + + rand io.Reader + + epochFunc epochFunc + hwAddrFunc hwAddrFunc + lastTime uint64 + clockSequence uint16 + hardwareAddr [6]byte +} + +func newRFC4122Generator() Generator { + return &rfc4122Generator{ + epochFunc: time.Now, + hwAddrFunc: defaultHWAddrFunc, + rand: rand.Reader, + } +} + +// NewV1 returns UUID based on current timestamp and MAC address. +func (g *rfc4122Generator) NewV1() (UUID, error) { + u := UUID{} + + timeNow, clockSeq, err := g.getClockSequence() + if err != nil { + return Nil, err + } + binary.BigEndian.PutUint32(u[0:], uint32(timeNow)) + binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32)) + binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48)) + binary.BigEndian.PutUint16(u[8:], clockSeq) + + hardwareAddr, err := g.getHardwareAddr() + if err != nil { + return Nil, err + } + copy(u[10:], hardwareAddr) + + u.SetVersion(V1) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV2 returns DCE Security UUID based on POSIX UID/GID. +func (g *rfc4122Generator) NewV2(domain byte) (UUID, error) { + u, err := g.NewV1() + if err != nil { + return Nil, err + } + + switch domain { + case DomainPerson: + binary.BigEndian.PutUint32(u[:], posixUID) + case DomainGroup: + binary.BigEndian.PutUint32(u[:], posixGID) + } + + u[9] = domain + + u.SetVersion(V2) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV3 returns UUID based on MD5 hash of namespace UUID and name. +func (g *rfc4122Generator) NewV3(ns UUID, name string) UUID { + u := newFromHash(md5.New(), ns, name) + u.SetVersion(V3) + u.SetVariant(VariantRFC4122) + + return u +} + +// NewV4 returns random generated UUID. +func (g *rfc4122Generator) NewV4() (UUID, error) { + u := UUID{} + if _, err := g.rand.Read(u[:]); err != nil { + return Nil, err + } + u.SetVersion(V4) + u.SetVariant(VariantRFC4122) + + return u, nil +} + +// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name. +func (g *rfc4122Generator) NewV5(ns UUID, name string) UUID { + u := newFromHash(sha1.New(), ns, name) + u.SetVersion(V5) + u.SetVariant(VariantRFC4122) + + return u +} + +// Returns epoch and clock sequence. +func (g *rfc4122Generator) getClockSequence() (uint64, uint16, error) { + var err error + g.clockSequenceOnce.Do(func() { + buf := make([]byte, 2) + if _, err = g.rand.Read(buf); err != nil { + return + } + g.clockSequence = binary.BigEndian.Uint16(buf) + }) + if err != nil { + return 0, 0, err + } + + g.storageMutex.Lock() + defer g.storageMutex.Unlock() + + timeNow := g.getEpoch() + // Clock didn't change since last UUID generation. + // Should increase clock sequence. + if timeNow <= g.lastTime { + g.clockSequence++ + } + g.lastTime = timeNow + + return timeNow, g.clockSequence, nil +} + +// Returns hardware address. +func (g *rfc4122Generator) getHardwareAddr() ([]byte, error) { + var err error + g.hardwareAddrOnce.Do(func() { + if hwAddr, err := g.hwAddrFunc(); err == nil { + copy(g.hardwareAddr[:], hwAddr) + return + } + + // Initialize hardwareAddr randomly in case + // of real network interfaces absence. + if _, err = g.rand.Read(g.hardwareAddr[:]); err != nil { + return + } + // Set multicast bit as recommended by RFC 4122 + g.hardwareAddr[0] |= 0x01 + }) + if err != nil { + return []byte{}, err + } + return g.hardwareAddr[:], nil +} + +// Returns difference in 100-nanosecond intervals between +// UUID epoch (October 15, 1582) and current time. +func (g *rfc4122Generator) getEpoch() uint64 { + return epochStart + uint64(g.epochFunc().UnixNano()/100) +} + +// Returns UUID based on hashing of namespace UUID and name. +func newFromHash(h hash.Hash, ns UUID, name string) UUID { + u := UUID{} + h.Write(ns[:]) + h.Write([]byte(name)) + copy(u[:], h.Sum(nil)) + + return u +} + +// Returns hardware address. +func defaultHWAddrFunc() (net.HardwareAddr, error) { + ifaces, err := net.Interfaces() + if err != nil { + return []byte{}, err + } + for _, iface := range ifaces { + if len(iface.HardwareAddr) >= 6 { + return iface.HardwareAddr, nil + } + } + return []byte{}, fmt.Errorf("uuid: no HW address found") +} diff --git a/vendor/github.com/satori/go.uuid/sql.go b/vendor/github.com/satori/go.uuid/sql.go new file mode 100644 index 0000000..56759d3 --- /dev/null +++ b/vendor/github.com/satori/go.uuid/sql.go @@ -0,0 +1,78 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +package uuid + +import ( + "database/sql/driver" + "fmt" +) + +// Value implements the driver.Valuer interface. +func (u UUID) Value() (driver.Value, error) { + return u.String(), nil +} + +// Scan implements the sql.Scanner interface. +// A 16-byte slice is handled by UnmarshalBinary, while +// a longer byte slice or a string is handled by UnmarshalText. +func (u *UUID) Scan(src interface{}) error { + switch src := src.(type) { + case []byte: + if len(src) == Size { + return u.UnmarshalBinary(src) + } + return u.UnmarshalText(src) + + case string: + return u.UnmarshalText([]byte(src)) + } + + return fmt.Errorf("uuid: cannot convert %T to UUID", src) +} + +// NullUUID can be used with the standard sql package to represent a +// UUID value that can be NULL in the database +type NullUUID struct { + UUID UUID + Valid bool +} + +// Value implements the driver.Valuer interface. +func (u NullUUID) Value() (driver.Value, error) { + if !u.Valid { + return nil, nil + } + // Delegate to UUID Value function + return u.UUID.Value() +} + +// Scan implements the sql.Scanner interface. +func (u *NullUUID) Scan(src interface{}) error { + if src == nil { + u.UUID, u.Valid = Nil, false + return nil + } + + // Delegate to UUID Scan function + u.Valid = true + return u.UUID.Scan(src) +} diff --git a/vendor/github.com/satori/go.uuid/uuid.go b/vendor/github.com/satori/go.uuid/uuid.go new file mode 100644 index 0000000..a2b8e2c --- /dev/null +++ b/vendor/github.com/satori/go.uuid/uuid.go @@ -0,0 +1,161 @@ +// Copyright (C) 2013-2018 by Maxim Bublis +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Package uuid provides implementation of Universally Unique Identifier (UUID). +// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and +// version 2 (as specified in DCE 1.1). +package uuid + +import ( + "bytes" + "encoding/hex" +) + +// Size of a UUID in bytes. +const Size = 16 + +// UUID representation compliant with specification +// described in RFC 4122. +type UUID [Size]byte + +// UUID versions +const ( + _ byte = iota + V1 + V2 + V3 + V4 + V5 +) + +// UUID layout variants. +const ( + VariantNCS byte = iota + VariantRFC4122 + VariantMicrosoft + VariantFuture +) + +// UUID DCE domains. +const ( + DomainPerson = iota + DomainGroup + DomainOrg +) + +// String parse helpers. +var ( + urnPrefix = []byte("urn:uuid:") + byteGroups = []int{8, 4, 4, 4, 12} +) + +// Nil is special form of UUID that is specified to have all +// 128 bits set to zero. +var Nil = UUID{} + +// Predefined namespace UUIDs. +var ( + NamespaceDNS = Must(FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) + NamespaceURL = Must(FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) + NamespaceOID = Must(FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) + NamespaceX500 = Must(FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) +) + +// Equal returns true if u1 and u2 equals, otherwise returns false. +func Equal(u1 UUID, u2 UUID) bool { + return bytes.Equal(u1[:], u2[:]) +} + +// Version returns algorithm version used to generate UUID. +func (u UUID) Version() byte { + return u[6] >> 4 +} + +// Variant returns UUID layout variant. +func (u UUID) Variant() byte { + switch { + case (u[8] >> 7) == 0x00: + return VariantNCS + case (u[8] >> 6) == 0x02: + return VariantRFC4122 + case (u[8] >> 5) == 0x06: + return VariantMicrosoft + case (u[8] >> 5) == 0x07: + fallthrough + default: + return VariantFuture + } +} + +// Bytes returns bytes slice representation of UUID. +func (u UUID) Bytes() []byte { + return u[:] +} + +// Returns canonical string representation of UUID: +// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. +func (u UUID) String() string { + buf := make([]byte, 36) + + hex.Encode(buf[0:8], u[0:4]) + buf[8] = '-' + hex.Encode(buf[9:13], u[4:6]) + buf[13] = '-' + hex.Encode(buf[14:18], u[6:8]) + buf[18] = '-' + hex.Encode(buf[19:23], u[8:10]) + buf[23] = '-' + hex.Encode(buf[24:], u[10:]) + + return string(buf) +} + +// SetVersion sets version bits. +func (u *UUID) SetVersion(v byte) { + u[6] = (u[6] & 0x0f) | (v << 4) +} + +// SetVariant sets variant bits. +func (u *UUID) SetVariant(v byte) { + switch v { + case VariantNCS: + u[8] = (u[8]&(0xff>>1) | (0x00 << 7)) + case VariantRFC4122: + u[8] = (u[8]&(0xff>>2) | (0x02 << 6)) + case VariantMicrosoft: + u[8] = (u[8]&(0xff>>3) | (0x06 << 5)) + case VariantFuture: + fallthrough + default: + u[8] = (u[8]&(0xff>>3) | (0x07 << 5)) + } +} + +// Must is a helper that wraps a call to a function returning (UUID, error) +// and panics if the error is non-nil. It is intended for use in variable +// initializations such as +// var packageUUID = uuid.Must(uuid.FromString("123e4567-e89b-12d3-a456-426655440000")); +func Must(u UUID, err error) UUID { + if err != nil { + panic(err) + } + return u +} diff --git a/vendor/vendor.json b/vendor/vendor.json index df84cab..34e2e94 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -118,6 +118,12 @@ "revision": "14207d285c6c197daabb5c9793d63e7af9ab2d50", "revisionTime": "2017-02-01T02:35:40Z" }, + { + "checksumSHA1": "eDQ6f1EsNf+frcRO/9XukSEchm8=", + "path": "github.com/satori/go.uuid", + "revision": "36e9d2ebbde5e3f13ab2e25625fd453271d6522e", + "revisionTime": "2018-01-03T17:44:51Z" + }, { "checksumSHA1": "VCNCWOWM96WHwPmr/f4QhnszNGE=", "path": "github.com/valyala/bytebufferpool",