summaryrefslogtreecommitdiffstats
path: root/vendor
diff options
context:
space:
mode:
authorDavid Lu <david.lu97@outlook.com>2016-09-06 18:51:27 -0400
committerenahum <nahumhbl@gmail.com>2016-09-06 19:51:27 -0300
commit51501f920c092791c7d83ac7067874547a37c96a (patch)
tree8665cdc82c4fa99ba5c2b6743c66e0912fd53ddb /vendor
parent47d77d258961f95f4348b4745da062c08731b283 (diff)
downloadchat-51501f920c092791c7d83ac7067874547a37c96a.tar.gz
chat-51501f920c092791c7d83ac7067874547a37c96a.tar.bz2
chat-51501f920c092791c7d83ac7067874547a37c96a.zip
PLT-3753 Added Segment analytics (#3972)
Diffstat (limited to 'vendor')
-rw-r--r--vendor/github.com/jehiah/go-strftime/README.md4
-rw-r--r--vendor/github.com/jehiah/go-strftime/strftime.go71
-rw-r--r--vendor/github.com/jehiah/go-strftime/strftime_test.go40
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/Godeps.json17
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/Readme5
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/.gitignore2
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/.gitignore22
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/README.md4
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime.go71
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime_test.go40
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/AUTHORS5
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/LICENSE27
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid.go204
-rw-r--r--vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid_test.go102
-rw-r--r--vendor/github.com/segmentio/analytics-go/History.md67
-rw-r--r--vendor/github.com/segmentio/analytics-go/Makefile10
-rw-r--r--vendor/github.com/segmentio/analytics-go/Readme.md8
-rw-r--r--vendor/github.com/segmentio/analytics-go/analytics.go407
-rw-r--r--vendor/github.com/segmentio/analytics-go/analytics_test.go478
-rw-r--r--vendor/github.com/segmentio/analytics-go/circle.yml18
-rw-r--r--vendor/github.com/segmentio/analytics-go/cli/cli.go174
-rw-r--r--vendor/github.com/segmentio/analytics-go/examples/track.go36
-rw-r--r--vendor/github.com/segmentio/backo-go/README.md80
-rw-r--r--vendor/github.com/segmentio/backo-go/backo.go83
-rw-r--r--vendor/github.com/segmentio/backo-go/backo_test.go77
-rw-r--r--vendor/github.com/xtgo/uuid/AUTHORS5
-rw-r--r--vendor/github.com/xtgo/uuid/LICENSE27
-rw-r--r--vendor/github.com/xtgo/uuid/uuid.go204
-rw-r--r--vendor/github.com/xtgo/uuid/uuid_test.go102
29 files changed, 2390 insertions, 0 deletions
diff --git a/vendor/github.com/jehiah/go-strftime/README.md b/vendor/github.com/jehiah/go-strftime/README.md
new file mode 100644
index 000000000..8eb240384
--- /dev/null
+++ b/vendor/github.com/jehiah/go-strftime/README.md
@@ -0,0 +1,4 @@
+go-strftime
+===========
+
+go implementation of strftime \ No newline at end of file
diff --git a/vendor/github.com/jehiah/go-strftime/strftime.go b/vendor/github.com/jehiah/go-strftime/strftime.go
new file mode 100644
index 000000000..99e26716f
--- /dev/null
+++ b/vendor/github.com/jehiah/go-strftime/strftime.go
@@ -0,0 +1,71 @@
+// go implementation of strftime
+package strftime
+
+import (
+ "strings"
+ "time"
+)
+
+// taken from time/format.go
+var conversion = map[rune]string {
+ /*stdLongMonth */ 'B':"January",
+ /*stdMonth */ 'b': "Jan",
+ // stdNumMonth */ 'm': "1",
+ /*stdZeroMonth */ 'm': "01",
+ /*stdLongWeekDay */ 'A': "Monday",
+ /*stdWeekDay */ 'a': "Mon",
+ // stdDay */ 'd': "2",
+ // stdUnderDay */ 'd': "_2",
+ /*stdZeroDay */ 'd': "02",
+ /*stdHour */ 'H': "15",
+ // stdHour12 */ 'I': "3",
+ /*stdZeroHour12 */ 'I': "03",
+ // stdMinute */ 'M': "4",
+ /*stdZeroMinute */ 'M': "04",
+ // stdSecond */ 'S': "5",
+ /*stdZeroSecond */ 'S': "05",
+ /*stdLongYear */ 'Y': "2006",
+ /*stdYear */ 'y': "06",
+ /*stdPM */ 'p': "PM",
+ // stdpm */ 'p': "pm",
+ /*stdTZ */ 'Z': "MST",
+ // stdISO8601TZ */ 'z': "Z0700", // prints Z for UTC
+ // stdISO8601ColonTZ */ 'z': "Z07:00", // prints Z for UTC
+ /*stdNumTZ */ 'z': "-0700", // always numeric
+ // stdNumShortTZ */ 'b': "-07", // always numeric
+ // stdNumColonTZ */ 'b': "-07:00", // always numeric
+}
+
+// This is an alternative to time.Format because no one knows
+// what date 040305 is supposed to create when used as a 'layout' string
+// this takes standard strftime format options. For a complete list
+// of format options see http://strftime.org/
+func Format(format string, t time.Time) string {
+ retval := make([]byte, 0, len(format))
+ for i, ni := 0, 0; i < len(format); i = ni + 2 {
+ ni = strings.IndexByte(format[i:], '%')
+ if ni < 0 {
+ ni = len(format)
+ } else {
+ ni += i
+ }
+ retval = append(retval, []byte(format[i:ni])...)
+ if ni + 1 < len(format) {
+ c := format[ni + 1]
+ if c == '%' {
+ retval = append(retval, '%')
+ } else {
+ if layoutCmd, ok := conversion[rune(c)]; ok {
+ retval = append(retval, []byte(t.Format(layoutCmd))...)
+ } else {
+ retval = append(retval, '%', c)
+ }
+ }
+ } else {
+ if ni < len(format) {
+ retval = append(retval, '%')
+ }
+ }
+ }
+ return string(retval)
+}
diff --git a/vendor/github.com/jehiah/go-strftime/strftime_test.go b/vendor/github.com/jehiah/go-strftime/strftime_test.go
new file mode 100644
index 000000000..45cbca345
--- /dev/null
+++ b/vendor/github.com/jehiah/go-strftime/strftime_test.go
@@ -0,0 +1,40 @@
+package strftime
+
+import (
+ "time"
+ "fmt"
+ "testing"
+)
+
+func ExampleFormat() {
+ t := time.Unix(1340244776, 0)
+ utc, _ := time.LoadLocation("UTC")
+ t = t.In(utc)
+ fmt.Println(Format("%Y-%m-%d %H:%M:%S", t))
+ // Output:
+ // 2012-06-21 02:12:56
+}
+
+func TestNoLeadingPercentSign(t *testing.T) {
+ tm := time.Unix(1340244776, 0)
+ utc, _ := time.LoadLocation("UTC")
+ tm = tm.In(utc)
+ result := Format("aaabbb0123456789%Y", tm)
+ if result != "aaabbb01234567892012" {
+ t.Logf("%s != %s", result, "aaabbb01234567892012")
+ t.Fail()
+ }
+}
+
+
+func TestUnsupported(t *testing.T) {
+ tm := time.Unix(1340244776, 0)
+ utc, _ := time.LoadLocation("UTC")
+ tm = tm.In(utc)
+ result := Format("%0%1%%%2", tm)
+ if result != "%0%1%%2" {
+ t.Logf("%s != %s", result, "%0%1%%2")
+ t.Fail()
+ }
+}
+
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/Godeps.json b/vendor/github.com/segmentio/analytics-go/Godeps/Godeps.json
new file mode 100644
index 000000000..e1b79eb2d
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/Godeps.json
@@ -0,0 +1,17 @@
+{
+ "ImportPath": "github.com/segmentio/analytics-go",
+ "GoVersion": "go1.4.2",
+ "Packages": [
+ "./..."
+ ],
+ "Deps": [
+ {
+ "ImportPath": "github.com/jehiah/go-strftime",
+ "Rev": "834e15c05a45371503440cc195bbd05c9a0968d9"
+ },
+ {
+ "ImportPath": "github.com/xtgo/uuid",
+ "Rev": "a0b114877d4caeffbd7f87e3757c17fce570fea7"
+ }
+ ]
+}
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/Readme b/vendor/github.com/segmentio/analytics-go/Godeps/Readme
new file mode 100644
index 000000000..4cdaa53d5
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/Readme
@@ -0,0 +1,5 @@
+This directory tree is generated automatically by godep.
+
+Please do not edit.
+
+See https://github.com/tools/godep for more information.
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/.gitignore b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/.gitignore
new file mode 100644
index 000000000..f037d684e
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/.gitignore
@@ -0,0 +1,2 @@
+/pkg
+/bin
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/.gitignore b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/.gitignore
new file mode 100644
index 000000000..00268614f
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/.gitignore
@@ -0,0 +1,22 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/README.md b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/README.md
new file mode 100644
index 000000000..8eb240384
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/README.md
@@ -0,0 +1,4 @@
+go-strftime
+===========
+
+go implementation of strftime \ No newline at end of file
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime.go b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime.go
new file mode 100644
index 000000000..99e26716f
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime.go
@@ -0,0 +1,71 @@
+// go implementation of strftime
+package strftime
+
+import (
+ "strings"
+ "time"
+)
+
+// taken from time/format.go
+var conversion = map[rune]string {
+ /*stdLongMonth */ 'B':"January",
+ /*stdMonth */ 'b': "Jan",
+ // stdNumMonth */ 'm': "1",
+ /*stdZeroMonth */ 'm': "01",
+ /*stdLongWeekDay */ 'A': "Monday",
+ /*stdWeekDay */ 'a': "Mon",
+ // stdDay */ 'd': "2",
+ // stdUnderDay */ 'd': "_2",
+ /*stdZeroDay */ 'd': "02",
+ /*stdHour */ 'H': "15",
+ // stdHour12 */ 'I': "3",
+ /*stdZeroHour12 */ 'I': "03",
+ // stdMinute */ 'M': "4",
+ /*stdZeroMinute */ 'M': "04",
+ // stdSecond */ 'S': "5",
+ /*stdZeroSecond */ 'S': "05",
+ /*stdLongYear */ 'Y': "2006",
+ /*stdYear */ 'y': "06",
+ /*stdPM */ 'p': "PM",
+ // stdpm */ 'p': "pm",
+ /*stdTZ */ 'Z': "MST",
+ // stdISO8601TZ */ 'z': "Z0700", // prints Z for UTC
+ // stdISO8601ColonTZ */ 'z': "Z07:00", // prints Z for UTC
+ /*stdNumTZ */ 'z': "-0700", // always numeric
+ // stdNumShortTZ */ 'b': "-07", // always numeric
+ // stdNumColonTZ */ 'b': "-07:00", // always numeric
+}
+
+// This is an alternative to time.Format because no one knows
+// what date 040305 is supposed to create when used as a 'layout' string
+// this takes standard strftime format options. For a complete list
+// of format options see http://strftime.org/
+func Format(format string, t time.Time) string {
+ retval := make([]byte, 0, len(format))
+ for i, ni := 0, 0; i < len(format); i = ni + 2 {
+ ni = strings.IndexByte(format[i:], '%')
+ if ni < 0 {
+ ni = len(format)
+ } else {
+ ni += i
+ }
+ retval = append(retval, []byte(format[i:ni])...)
+ if ni + 1 < len(format) {
+ c := format[ni + 1]
+ if c == '%' {
+ retval = append(retval, '%')
+ } else {
+ if layoutCmd, ok := conversion[rune(c)]; ok {
+ retval = append(retval, []byte(t.Format(layoutCmd))...)
+ } else {
+ retval = append(retval, '%', c)
+ }
+ }
+ } else {
+ if ni < len(format) {
+ retval = append(retval, '%')
+ }
+ }
+ }
+ return string(retval)
+}
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime_test.go b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime_test.go
new file mode 100644
index 000000000..45cbca345
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/jehiah/go-strftime/strftime_test.go
@@ -0,0 +1,40 @@
+package strftime
+
+import (
+ "time"
+ "fmt"
+ "testing"
+)
+
+func ExampleFormat() {
+ t := time.Unix(1340244776, 0)
+ utc, _ := time.LoadLocation("UTC")
+ t = t.In(utc)
+ fmt.Println(Format("%Y-%m-%d %H:%M:%S", t))
+ // Output:
+ // 2012-06-21 02:12:56
+}
+
+func TestNoLeadingPercentSign(t *testing.T) {
+ tm := time.Unix(1340244776, 0)
+ utc, _ := time.LoadLocation("UTC")
+ tm = tm.In(utc)
+ result := Format("aaabbb0123456789%Y", tm)
+ if result != "aaabbb01234567892012" {
+ t.Logf("%s != %s", result, "aaabbb01234567892012")
+ t.Fail()
+ }
+}
+
+
+func TestUnsupported(t *testing.T) {
+ tm := time.Unix(1340244776, 0)
+ utc, _ := time.LoadLocation("UTC")
+ tm = tm.In(utc)
+ result := Format("%0%1%%%2", tm)
+ if result != "%0%1%%2" {
+ t.Logf("%s != %s", result, "%0%1%%2")
+ t.Fail()
+ }
+}
+
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/AUTHORS b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/AUTHORS
new file mode 100644
index 000000000..a6f045160
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/AUTHORS
@@ -0,0 +1,5 @@
+# This source file refers to The gocql Authors for copyright purposes.
+
+Christoph Hack <christoph@tux21b.org>
+Jonathan Rudenberg <jonathan@titanous.com>
+Thorsten von Eicken <tve@rightscale.com>
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/LICENSE b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/LICENSE
new file mode 100644
index 000000000..18d25f923
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 The gocql Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid.go b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid.go
new file mode 100644
index 000000000..a0fd7a5a5
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid.go
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 The gocql Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package uuid can be used to generate and parse universally unique
+// identifiers, a standardized format in the form of a 128 bit number.
+//
+// http://tools.ietf.org/html/rfc4122
+package uuid
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "errors"
+ "io"
+ "net"
+ "strconv"
+ "time"
+)
+
+type UUID [16]byte
+
+var hardwareAddr []byte
+
+const (
+ VariantNCSCompat = 0
+ VariantIETF = 2
+ VariantMicrosoft = 6
+ VariantFuture = 7
+)
+
+func init() {
+ if interfaces, err := net.Interfaces(); err == nil {
+ for _, i := range interfaces {
+ if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 {
+ hardwareAddr = i.HardwareAddr
+ break
+ }
+ }
+ }
+ if hardwareAddr == nil {
+ // If we failed to obtain the MAC address of the current computer,
+ // we will use a randomly generated 6 byte sequence instead and set
+ // the multicast bit as recommended in RFC 4122.
+ hardwareAddr = make([]byte, 6)
+ _, err := io.ReadFull(rand.Reader, hardwareAddr)
+ if err != nil {
+ panic(err)
+ }
+ hardwareAddr[0] = hardwareAddr[0] | 0x01
+ }
+}
+
+// Parse parses a 32 digit hexadecimal number (that might contain hyphens)
+// representing an UUID.
+func Parse(input string) (UUID, error) {
+ var u UUID
+ j := 0
+ for i := 0; i < len(input); i++ {
+ b := input[i]
+ switch {
+ default:
+ fallthrough
+ case j == 32:
+ goto err
+ case b == '-':
+ continue
+ case '0' <= b && b <= '9':
+ b -= '0'
+ case 'a' <= b && b <= 'f':
+ b -= 'a' - 10
+ case 'A' <= b && b <= 'F':
+ b -= 'A' - 10
+ }
+ u[j/2] |= b << byte(^j&1<<2)
+ j++
+ }
+ if j == 32 {
+ return u, nil
+ }
+err:
+ return UUID{}, errors.New("invalid UUID " + strconv.Quote(input))
+}
+
+// FromBytes converts a raw byte slice to an UUID. It will panic if the slice
+// isn't exactly 16 bytes long.
+func FromBytes(input []byte) UUID {
+ var u UUID
+ if len(input) != 16 {
+ panic("UUIDs must be exactly 16 bytes long")
+ }
+ copy(u[:], input)
+ return u
+}
+
+// NewRandom generates a totally random UUID (version 4) as described in
+// RFC 4122.
+func NewRandom() UUID {
+ var u UUID
+ io.ReadFull(rand.Reader, u[:])
+ u[6] &= 0x0F // clear version
+ u[6] |= 0x40 // set version to 4 (random uuid)
+ u[8] &= 0x3F // clear variant
+ u[8] |= 0x80 // set to IETF variant
+ return u
+}
+
+var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix()
+
+// NewTime generates a new time based UUID (version 1) as described in RFC
+// 4122. This UUID contains the MAC address of the node that generated the
+// UUID, a timestamp and a sequence number.
+func NewTime() UUID {
+ var u UUID
+
+ now := time.Now().In(time.UTC)
+ t := uint64(now.Unix()-timeBase)*10000000 + uint64(now.Nanosecond()/100)
+ u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t)
+ u[4], u[5] = byte(t>>40), byte(t>>32)
+ u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48)
+
+ var clockSeq [2]byte
+ io.ReadFull(rand.Reader, clockSeq[:])
+ u[8] = clockSeq[1]
+ u[9] = clockSeq[0]
+
+ copy(u[10:], hardwareAddr)
+
+ u[6] |= 0x10 // set version to 1 (time based uuid)
+ u[8] &= 0x3F // clear variant
+ u[8] |= 0x80 // set to IETF variant
+
+ return u
+}
+
+// String returns the UUID in it's canonical form, a 32 digit hexadecimal
+// number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
+func (u UUID) String() string {
+ buf := [36]byte{8: '-', 13: '-', 18: '-', 23: '-'}
+ hex.Encode(buf[0:], u[0:4])
+ hex.Encode(buf[9:], u[4:6])
+ hex.Encode(buf[14:], u[6:8])
+ hex.Encode(buf[19:], u[8:10])
+ hex.Encode(buf[24:], u[10:])
+ return string(buf[:])
+}
+
+// Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits
+// (16 bytes) long.
+func (u UUID) Bytes() []byte {
+ return u[:]
+}
+
+// Variant returns the variant of this UUID. This package will only generate
+// UUIDs in the IETF variant.
+func (u UUID) Variant() int {
+ x := u[8]
+ switch byte(0) {
+ case x & 0x80:
+ return VariantNCSCompat
+ case x & 0x40:
+ return VariantIETF
+ case x & 0x20:
+ return VariantMicrosoft
+ }
+ return VariantFuture
+}
+
+// Version extracts the version of this UUID variant. The RFC 4122 describes
+// five kinds of UUIDs.
+func (u UUID) Version() int {
+ return int(u[6] & 0xF0 >> 4)
+}
+
+// Node extracts the MAC address of the node who generated this UUID. It will
+// return nil if the UUID is not a time based UUID (version 1).
+func (u UUID) Node() []byte {
+ if u.Version() != 1 {
+ return nil
+ }
+ return u[10:]
+}
+
+// Timestamp extracts the timestamp information from a time based UUID
+// (version 1).
+func (u UUID) Timestamp() uint64 {
+ if u.Version() != 1 {
+ return 0
+ }
+ return uint64(u[0])<<24 + uint64(u[1])<<16 + uint64(u[2])<<8 +
+ uint64(u[3]) + uint64(u[4])<<40 + uint64(u[5])<<32 +
+ uint64(u[7])<<48 + uint64(u[6]&0x0F)<<56
+}
+
+// Time is like Timestamp, except that it returns a time.Time.
+func (u UUID) Time() time.Time {
+ t := u.Timestamp()
+ if t == 0 {
+ return time.Time{}
+ }
+ sec := t / 10000000
+ nsec := t - sec
+ return time.Unix(int64(sec)+timeBase, int64(nsec))
+}
diff --git a/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid_test.go b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid_test.go
new file mode 100644
index 000000000..3cef4a31a
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Godeps/_workspace/src/github.com/xtgo/uuid/uuid_test.go
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The gocql Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestNil(t *testing.T) {
+ var uuid UUID
+ want, got := "00000000-0000-0000-0000-000000000000", uuid.String()
+ if want != got {
+ t.Fatalf("TestNil: expected %q got %q", want, got)
+ }
+}
+
+var tests = []struct {
+ input string
+ variant int
+ version int
+}{
+ {"b4f00409-cef8-4822-802c-deb20704c365", VariantIETF, 4},
+ {"f81d4fae-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1},
+ {"00000000-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1},
+ {"3051a8d7-aea7-1801-e0bf-bc539dd60cf3", VariantFuture, 1},
+ {"3051a8d7-aea7-2801-e0bf-bc539dd60cf3", VariantFuture, 2},
+ {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 3},
+ {"3051a8d7-aea7-4801-e0bf-bc539dd60cf3", VariantFuture, 4},
+ {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 5},
+ {"d0e817e1-e4b1-1801-3fe6-b4b60ccecf9d", VariantNCSCompat, 0},
+ {"d0e817e1-e4b1-1801-bfe6-b4b60ccecf9d", VariantIETF, 1},
+ {"d0e817e1-e4b1-1801-dfe6-b4b60ccecf9d", VariantMicrosoft, 0},
+ {"d0e817e1-e4b1-1801-ffe6-b4b60ccecf9d", VariantFuture, 0},
+}
+
+func TestPredefined(t *testing.T) {
+ for i := range tests {
+ uuid, err := Parse(tests[i].input)
+ if err != nil {
+ t.Errorf("Parse #%d: %v", i, err)
+ continue
+ }
+
+ if str := uuid.String(); str != tests[i].input {
+ t.Errorf("String #%d: expected %q got %q", i, tests[i].input, str)
+ continue
+ }
+
+ if variant := uuid.Variant(); variant != tests[i].variant {
+ t.Errorf("Variant #%d: expected %d got %d", i, tests[i].variant, variant)
+ }
+
+ if tests[i].variant == VariantIETF {
+ if version := uuid.Version(); version != tests[i].version {
+ t.Errorf("Version #%d: expected %d got %d", i, tests[i].version, version)
+ }
+ }
+ }
+}
+
+func TestNewRandom(t *testing.T) {
+ for i := 0; i < 20; i++ {
+ uuid := NewRandom()
+
+ if variant := uuid.Variant(); variant != VariantIETF {
+ t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant)
+ }
+ if version := uuid.Version(); version != 4 {
+ t.Errorf("wrong version. expected %d got %d", 4, version)
+ }
+ }
+}
+
+func TestNewTime(t *testing.T) {
+ var node []byte
+ timestamp := uint64(0)
+ for i := 0; i < 20; i++ {
+ uuid := NewTime()
+
+ if variant := uuid.Variant(); variant != VariantIETF {
+ t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant)
+ }
+ if version := uuid.Version(); version != 1 {
+ t.Errorf("wrong version. expected %d got %d", 1, version)
+ }
+
+ if n := uuid.Node(); !bytes.Equal(n, node) && i > 0 {
+ t.Errorf("wrong node. expected %x, got %x", node, n)
+ } else if i == 0 {
+ node = n
+ }
+
+ ts := uuid.Timestamp()
+ if ts < timestamp {
+ t.Errorf("timestamps must grow")
+ }
+ timestamp = ts
+ }
+}
diff --git a/vendor/github.com/segmentio/analytics-go/History.md b/vendor/github.com/segmentio/analytics-go/History.md
new file mode 100644
index 000000000..e8538a0cc
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/History.md
@@ -0,0 +1,67 @@
+
+v2.1.1 / 2016-04-26
+===================
+
+ * Fix blocking the goroutine when Close is the first call.
+ * Fix blocking the goroutine when the message queue fills up.
+
+v2.1.0 / 2015-12-28
+===================
+
+ * Add ability to set custom timestamps for messages.
+ * Add ability to set a custom `net/http` client.
+ * Add ability to set a custom logger.
+ * Fix edge case when client would try to upload no messages.
+ * Properly upload in-flight messages when client is asked to shutdown.
+ * Add ability to set `.integrations` field on messages.
+ * Fix resource leak with interval ticker after shutdown.
+ * Add retries and back-off when uploading messages.
+ * Add ability to set custom flush interval.
+
+v2.0.0 / 2015-02-03
+===================
+
+ * rewrite with breaking API changes
+
+v1.2.0 / 2014-09-03
+==================
+
+ * add public .Flush() method
+ * rename .Stop() to .Close()
+
+v1.1.0 / 2014-09-02
+==================
+
+ * add client.Stop() to flash/wait. Closes #7
+
+v1.0.0 / 2014-08-26
+==================
+
+ * fix response close
+ * change comments to be more go-like
+ * change uuid libraries
+
+0.1.2 / 2014-06-11
+==================
+
+ * add runnable example
+ * fix: close body
+
+0.1.1 / 2014-05-31
+==================
+
+ * refactor locking
+
+0.1.0 / 2014-05-22
+==================
+
+ * replace Debug option with debug package
+
+0.0.2 / 2014-05-20
+==================
+
+ * add .Start()
+ * add mutexes
+ * rename BufferSize to FlushAt and FlushInterval to FlushAfter
+ * lower FlushInterval to 5 seconds
+ * lower BufferSize to 20 to match other clients
diff --git a/vendor/github.com/segmentio/analytics-go/Makefile b/vendor/github.com/segmentio/analytics-go/Makefile
new file mode 100644
index 000000000..e896b1b5f
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Makefile
@@ -0,0 +1,10 @@
+vet:
+ @godep go vet ./...
+
+build:
+ @godep go build
+
+test:
+ @godep go test -race -cover ./...
+
+.PHONY: vet build test
diff --git a/vendor/github.com/segmentio/analytics-go/Readme.md b/vendor/github.com/segmentio/analytics-go/Readme.md
new file mode 100644
index 000000000..b521cbe63
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/Readme.md
@@ -0,0 +1,8 @@
+# analytics-go
+
+ Segment analytics client for Go. For additional documentation
+ visit [https://segment.com/docs/libraries/go](https://segment.com/docs/libraries/go/) or view the [godocs](http://godoc.org/github.com/segmentio/analytics-go).
+
+## License
+
+ MIT
diff --git a/vendor/github.com/segmentio/analytics-go/analytics.go b/vendor/github.com/segmentio/analytics-go/analytics.go
new file mode 100644
index 000000000..6ec93fcbf
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/analytics.go
@@ -0,0 +1,407 @@
+package analytics
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "sync"
+
+ "bytes"
+ "encoding/json"
+ "errors"
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/jehiah/go-strftime"
+ "github.com/segmentio/backo-go"
+ "github.com/xtgo/uuid"
+)
+
+// Version of the client.
+const Version = "2.1.0"
+
+// Endpoint for the Segment API.
+const Endpoint = "https://api.segment.io"
+
+// DefaultContext of message batches.
+var DefaultContext = map[string]interface{}{
+ "library": map[string]interface{}{
+ "name": "analytics-go",
+ "version": Version,
+ },
+}
+
+// Backoff policy.
+var Backo = backo.DefaultBacko()
+
+// Message interface.
+type message interface {
+ setMessageId(string)
+ setTimestamp(string)
+}
+
+// Message fields common to all.
+type Message struct {
+ Type string `json:"type,omitempty"`
+ MessageId string `json:"messageId,omitempty"`
+ Timestamp string `json:"timestamp,omitempty"`
+ SentAt string `json:"sentAt,omitempty"`
+}
+
+// Batch message.
+type Batch struct {
+ Context map[string]interface{} `json:"context,omitempty"`
+ Messages []interface{} `json:"batch"`
+ Message
+}
+
+// Identify message.
+type Identify struct {
+ Context map[string]interface{} `json:"context,omitempty"`
+ Integrations map[string]interface{} `json:"integrations,omitempty"`
+ Traits map[string]interface{} `json:"traits,omitempty"`
+ AnonymousId string `json:"anonymousId,omitempty"`
+ UserId string `json:"userId,omitempty"`
+ Message
+}
+
+// Group message.
+type Group struct {
+ Context map[string]interface{} `json:"context,omitempty"`
+ Integrations map[string]interface{} `json:"integrations,omitempty"`
+ Traits map[string]interface{} `json:"traits,omitempty"`
+ AnonymousId string `json:"anonymousId,omitempty"`
+ UserId string `json:"userId,omitempty"`
+ GroupId string `json:"groupId"`
+ Message
+}
+
+// Track message.
+type Track struct {
+ Context map[string]interface{} `json:"context,omitempty"`
+ Integrations map[string]interface{} `json:"integrations,omitempty"`
+ Properties map[string]interface{} `json:"properties,omitempty"`
+ AnonymousId string `json:"anonymousId,omitempty"`
+ UserId string `json:"userId,omitempty"`
+ Event string `json:"event"`
+ Message
+}
+
+// Page message.
+type Page struct {
+ Context map[string]interface{} `json:"context,omitempty"`
+ Integrations map[string]interface{} `json:"integrations,omitempty"`
+ Traits map[string]interface{} `json:"properties,omitempty"`
+ AnonymousId string `json:"anonymousId,omitempty"`
+ UserId string `json:"userId,omitempty"`
+ Category string `json:"category,omitempty"`
+ Name string `json:"name,omitempty"`
+ Message
+}
+
+// Alias message.
+type Alias struct {
+ PreviousId string `json:"previousId"`
+ UserId string `json:"userId"`
+ Message
+}
+
+// Client which batches messages and flushes at the given Interval or
+// when the Size limit is exceeded. Set Verbose to true to enable
+// logging output.
+type Client struct {
+ Endpoint string
+ // Interval represents the duration at which messages are flushed. It may be
+ // configured only before any messages are enqueued.
+ Interval time.Duration
+ Size int
+ Logger *log.Logger
+ Verbose bool
+ Client http.Client
+ key string
+ msgs chan interface{}
+ quit chan struct{}
+ shutdown chan struct{}
+ uid func() string
+ now func() time.Time
+ once sync.Once
+ wg sync.WaitGroup
+
+ // These synchronization primitives are used to control how many goroutines
+ // are spawned by the client for uploads.
+ upmtx sync.Mutex
+ upcond sync.Cond
+ upcount int
+}
+
+// New client with write key.
+func New(key string) *Client {
+ c := &Client{
+ Endpoint: Endpoint,
+ Interval: 5 * time.Second,
+ Size: 250,
+ Logger: log.New(os.Stderr, "segment ", log.LstdFlags),
+ Verbose: false,
+ Client: *http.DefaultClient,
+ key: key,
+ msgs: make(chan interface{}, 100),
+ quit: make(chan struct{}),
+ shutdown: make(chan struct{}),
+ now: time.Now,
+ uid: uid,
+ }
+
+ c.upcond.L = &c.upmtx
+ return c
+}
+
+// Alias buffers an "alias" message.
+func (c *Client) Alias(msg *Alias) error {
+ if msg.UserId == "" {
+ return errors.New("You must pass a 'userId'.")
+ }
+
+ if msg.PreviousId == "" {
+ return errors.New("You must pass a 'previousId'.")
+ }
+
+ msg.Type = "alias"
+ c.queue(msg)
+
+ return nil
+}
+
+// Page buffers an "page" message.
+func (c *Client) Page(msg *Page) error {
+ if msg.UserId == "" && msg.AnonymousId == "" {
+ return errors.New("You must pass either an 'anonymousId' or 'userId'.")
+ }
+
+ msg.Type = "page"
+ c.queue(msg)
+
+ return nil
+}
+
+// Group buffers an "group" message.
+func (c *Client) Group(msg *Group) error {
+ if msg.GroupId == "" {
+ return errors.New("You must pass a 'groupId'.")
+ }
+
+ if msg.UserId == "" && msg.AnonymousId == "" {
+ return errors.New("You must pass either an 'anonymousId' or 'userId'.")
+ }
+
+ msg.Type = "group"
+ c.queue(msg)
+
+ return nil
+}
+
+// Identify buffers an "identify" message.
+func (c *Client) Identify(msg *Identify) error {
+ if msg.UserId == "" && msg.AnonymousId == "" {
+ return errors.New("You must pass either an 'anonymousId' or 'userId'.")
+ }
+
+ msg.Type = "identify"
+ c.queue(msg)
+
+ return nil
+}
+
+// Track buffers an "track" message.
+func (c *Client) Track(msg *Track) error {
+ if msg.Event == "" {
+ return errors.New("You must pass 'event'.")
+ }
+
+ if msg.UserId == "" && msg.AnonymousId == "" {
+ return errors.New("You must pass either an 'anonymousId' or 'userId'.")
+ }
+
+ msg.Type = "track"
+ c.queue(msg)
+
+ return nil
+}
+
+func (c *Client) startLoop() {
+ go c.loop()
+}
+
+// Queue message.
+func (c *Client) queue(msg message) {
+ c.once.Do(c.startLoop)
+ msg.setMessageId(c.uid())
+ msg.setTimestamp(timestamp(c.now()))
+ c.msgs <- msg
+}
+
+// Close and flush metrics.
+func (c *Client) Close() error {
+ c.once.Do(c.startLoop)
+ c.quit <- struct{}{}
+ close(c.msgs)
+ <-c.shutdown
+ return nil
+}
+
+func (c *Client) sendAsync(msgs []interface{}) {
+ c.upmtx.Lock()
+ for c.upcount == 1000 {
+ c.upcond.Wait()
+ }
+ c.upcount++
+ c.upmtx.Unlock()
+ c.wg.Add(1)
+ go func() {
+ err := c.send(msgs)
+ if err != nil {
+ c.logf(err.Error())
+ }
+ c.upmtx.Lock()
+ c.upcount--
+ c.upcond.Signal()
+ c.upmtx.Unlock()
+ c.wg.Done()
+ }()
+}
+
+// Send batch request.
+func (c *Client) send(msgs []interface{}) error {
+ if len(msgs) == 0 {
+ return nil
+ }
+
+ batch := new(Batch)
+ batch.Messages = msgs
+ batch.MessageId = c.uid()
+ batch.SentAt = timestamp(c.now())
+ batch.Context = DefaultContext
+
+ b, err := json.Marshal(batch)
+ if err != nil {
+ return fmt.Errorf("error marshalling msgs: %s", err)
+ }
+
+ for i := 0; i < 10; i++ {
+ if err = c.upload(b); err == nil {
+ return nil
+ }
+ Backo.Sleep(i)
+ }
+
+ return err
+}
+
+// Upload serialized batch message.
+func (c *Client) upload(b []byte) error {
+ url := c.Endpoint + "/v1/batch"
+ req, err := http.NewRequest("POST", url, bytes.NewReader(b))
+ if err != nil {
+ return fmt.Errorf("error creating request: %s", err)
+ }
+
+ req.Header.Add("User-Agent", "analytics-go (version: "+Version+")")
+ req.Header.Add("Content-Type", "application/json")
+ req.Header.Add("Content-Length", string(len(b)))
+ req.SetBasicAuth(c.key, "")
+
+ res, err := c.Client.Do(req)
+ if err != nil {
+ return fmt.Errorf("error sending request: %s", err)
+ }
+ defer res.Body.Close()
+
+ if res.StatusCode < 400 {
+ c.verbose("response %s", res.Status)
+ return nil
+ }
+
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return fmt.Errorf("error reading response body: %s", err)
+ }
+
+ return fmt.Errorf("response %s: %d – %s", res.Status, res.StatusCode, string(body))
+}
+
+// Batch loop.
+func (c *Client) loop() {
+ var msgs []interface{}
+ tick := time.NewTicker(c.Interval)
+
+ for {
+ select {
+ case msg := <-c.msgs:
+ c.verbose("buffer (%d/%d) %v", len(msgs), c.Size, msg)
+ msgs = append(msgs, msg)
+ if len(msgs) == c.Size {
+ c.verbose("exceeded %d messages – flushing", c.Size)
+ c.sendAsync(msgs)
+ msgs = make([]interface{}, 0, c.Size)
+ }
+ case <-tick.C:
+ if len(msgs) > 0 {
+ c.verbose("interval reached - flushing %d", len(msgs))
+ c.sendAsync(msgs)
+ msgs = make([]interface{}, 0, c.Size)
+ } else {
+ c.verbose("interval reached – nothing to send")
+ }
+ case <-c.quit:
+ tick.Stop()
+ c.verbose("exit requested – draining msgs")
+ // drain the msg channel.
+ for msg := range c.msgs {
+ c.verbose("buffer (%d/%d) %v", len(msgs), c.Size, msg)
+ msgs = append(msgs, msg)
+ }
+ c.verbose("exit requested – flushing %d", len(msgs))
+ c.sendAsync(msgs)
+ c.wg.Wait()
+ c.verbose("exit")
+ c.shutdown <- struct{}{}
+ return
+ }
+ }
+}
+
+// Verbose log.
+func (c *Client) verbose(msg string, args ...interface{}) {
+ if c.Verbose {
+ c.Logger.Printf(msg, args...)
+ }
+}
+
+// Unconditional log.
+func (c *Client) logf(msg string, args ...interface{}) {
+ c.Logger.Printf(msg, args...)
+}
+
+// Set message timestamp if one is not already set.
+func (m *Message) setTimestamp(s string) {
+ if m.Timestamp == "" {
+ m.Timestamp = s
+ }
+}
+
+// Set message id.
+func (m *Message) setMessageId(s string) {
+ if m.MessageId == "" {
+ m.MessageId = s
+ }
+}
+
+// Return formatted timestamp.
+func timestamp(t time.Time) string {
+ return strftime.Format("%Y-%m-%dT%H:%M:%S%z", t)
+}
+
+// Return uuid string.
+func uid() string {
+ return uuid.NewRandom().String()
+}
diff --git a/vendor/github.com/segmentio/analytics-go/analytics_test.go b/vendor/github.com/segmentio/analytics-go/analytics_test.go
new file mode 100644
index 000000000..c6f0dc4f2
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/analytics_test.go
@@ -0,0 +1,478 @@
+package analytics
+
+import "net/http/httptest"
+import "encoding/json"
+import "net/http"
+import "testing"
+import "bytes"
+import "time"
+import "fmt"
+import "io"
+
+func mockId() string { return "I'm unique" }
+
+func mockTime() time.Time {
+ // time.Unix(0, 0) fails on Circle
+ return time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
+}
+
+func mockServer() (chan []byte, *httptest.Server) {
+ done := make(chan []byte, 1)
+
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ buf := bytes.NewBuffer(nil)
+ io.Copy(buf, r.Body)
+
+ var v interface{}
+ err := json.Unmarshal(buf.Bytes(), &v)
+ if err != nil {
+ panic(err)
+ }
+
+ b, err := json.MarshalIndent(v, "", " ")
+ if err != nil {
+ panic(err)
+ }
+
+ done <- b
+ }))
+
+ return done, server
+}
+
+func ExampleTrack() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+ client.Size = 1
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ })
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleClose() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ })
+
+ client.Close()
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleInterval() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ })
+
+ // Will flush in 5 seconds (default interval).
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleTrackWithTimestampSet() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+ client.Size = 1
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ Message: Message{
+ Timestamp: timestamp(time.Date(2015, time.July, 10, 23, 0, 0, 0, time.UTC)),
+ },
+ })
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2015-07-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleTrackWithMessageIdSet() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+ client.Size = 1
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ Message: Message{
+ MessageId: "abc",
+ },
+ })
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "messageId": "abc",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleTrack_context() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+ client.Size = 1
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ Context: map[string]interface{}{
+ "whatever": "here",
+ },
+ })
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "context": {
+ // "whatever": "here"
+ // },
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleTrack_many() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+ client.Size = 3
+
+ for i := 0; i < 5; i++ {
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": i,
+ },
+ })
+ }
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "version": 0
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // },
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "version": 1
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // },
+ // {
+ // "event": "Download",
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "version": 2
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+func ExampleTrackWithIntegrations() {
+ body, server := mockServer()
+ defer server.Close()
+
+ client := New("h97jamjwbh")
+ client.Endpoint = server.URL
+ client.now = mockTime
+ client.uid = mockId
+ client.Size = 1
+
+ client.Track(&Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ Integrations: map[string]interface{}{
+ "All": true,
+ "Intercom": false,
+ "Mixpanel": true,
+ },
+ })
+
+ fmt.Printf("%s\n", <-body)
+ // Output:
+ // {
+ // "batch": [
+ // {
+ // "event": "Download",
+ // "integrations": {
+ // "All": true,
+ // "Intercom": false,
+ // "Mixpanel": true
+ // },
+ // "messageId": "I'm unique",
+ // "properties": {
+ // "application": "Segment Desktop",
+ // "platform": "osx",
+ // "version": "1.1.0"
+ // },
+ // "timestamp": "2009-11-10T23:00:00+0000",
+ // "type": "track",
+ // "userId": "123456"
+ // }
+ // ],
+ // "context": {
+ // "library": {
+ // "name": "analytics-go",
+ // "version": "2.1.0"
+ // }
+ // },
+ // "messageId": "I'm unique",
+ // "sentAt": "2009-11-10T23:00:00+0000"
+ // }
+}
+
+// Tests that calling Close right after creating the client object doesn't
+// block.
+// Bug: https://github.com/segmentio/analytics-go/issues/43
+func TestCloseFinish(_ *testing.T) {
+ c := New("test")
+ c.Close()
+}
diff --git a/vendor/github.com/segmentio/analytics-go/circle.yml b/vendor/github.com/segmentio/analytics-go/circle.yml
new file mode 100644
index 000000000..11733e196
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/circle.yml
@@ -0,0 +1,18 @@
+machine:
+ environment:
+ IMPORT_PATH: "github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME"
+
+dependencies:
+ pre:
+ - go get github.com/tools/godep
+
+ override:
+ - mkdir -p "$GOPATH/src/$IMPORT_PATH"
+ - rsync -azC --delete ./ "$GOPATH/src/$IMPORT_PATH/"
+
+test:
+ pre:
+ - make vet
+
+ override:
+ - make test
diff --git a/vendor/github.com/segmentio/analytics-go/cli/cli.go b/vendor/github.com/segmentio/analytics-go/cli/cli.go
new file mode 100644
index 000000000..9d1f940c9
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/cli/cli.go
@@ -0,0 +1,174 @@
+package main
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "reflect"
+
+ "github.com/segmentio/analytics-go"
+ "github.com/tj/docopt"
+)
+
+const Usage = `
+Analytics Go CLI
+
+Usage:
+ analytics track <event> [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
+ analytics screen <name> [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
+ analytics page <name> [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
+ analytics identify [--traits=<traits>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
+ analytics group --groupId=<groupId> [--traits=<traits>] [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--userId=<userId>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
+ analytics alias --userId=<userId> --previousId=<previousId> [--traits=<traits>] [--properties=<properties>] [--context=<context>] [--writeKey=<writeKey>] [--anonymousId=<anonymousId>] [--integrations=<integrations>] [--timestamp=<timestamp>]
+ analytics -h | --help
+ analytics --version
+
+Options:
+ -h --help Show this screen.
+ --version Show version.
+`
+
+func main() {
+ arguments, err := docopt.Parse(Usage, nil, true, "Anaytics Go CLI", false)
+ check(err)
+
+ writeKey := getOptionalString(arguments, "--writeKey")
+ if writeKey == "" {
+ writeKey = os.Getenv("SEGMENT_WRITE_KEY")
+ if writeKey == "" {
+ log.Fatal("either $SEGMENT_WRITE_KEY or --writeKey must be provided")
+ }
+ }
+
+ client := analytics.New(writeKey)
+ client.Size = 1
+ client.Verbose = true
+
+ if arguments["track"].(bool) {
+ m := &analytics.Track{
+ Event: arguments["<event>"].(string),
+ }
+ properties := getOptionalString(arguments, "--properties")
+ if properties != "" {
+ var parsedProperties map[string]interface{}
+ err := json.Unmarshal([]byte(properties), &parsedProperties)
+ check(err)
+ m.Properties = parsedProperties
+ }
+
+ setCommonFields(m, arguments)
+
+ check(client.Track(m))
+ }
+
+ if arguments["screen"].(bool) || arguments["page"].(bool) {
+ m := &analytics.Page{
+ Name: arguments["<name>"].(string),
+ }
+ /* Bug in Go library - page has traits not properties.
+ properties := getOptionalString(arguments, "--properties")
+ if properties != "" {
+ var parsedProperties map[string]interface{}
+ err := json.Unmarshal([]byte(properties), &parsedProperties)
+ check(err)
+ t.Properties = parsedProperties
+ }
+ */
+
+ setCommonFields(m, arguments)
+
+ check(client.Page(m))
+ }
+
+ if arguments["identify"].(bool) {
+ m := &analytics.Identify{}
+ traits := getOptionalString(arguments, "--traits")
+ if traits != "" {
+ var parsedTraits map[string]interface{}
+ err := json.Unmarshal([]byte(traits), &parsedTraits)
+ check(err)
+ m.Traits = parsedTraits
+ }
+
+ setCommonFields(m, arguments)
+
+ check(client.Identify(m))
+ }
+
+ if arguments["group"].(bool) {
+ m := &analytics.Group{
+ GroupId: arguments["--groupId"].(string),
+ }
+ traits := getOptionalString(arguments, "--traits")
+ if traits != "" {
+ var parsedTraits map[string]interface{}
+ err := json.Unmarshal([]byte(traits), &parsedTraits)
+ check(err)
+ m.Traits = parsedTraits
+ }
+
+ setCommonFields(m, arguments)
+
+ check(client.Group(m))
+ }
+
+ if arguments["alias"].(bool) {
+ m := &analytics.Alias{
+ PreviousId: arguments["--previousId"].(string),
+ }
+
+ setCommonFields(m, arguments)
+
+ check(client.Alias(m))
+ }
+
+ client.Close()
+}
+
+func setCommonFields(message interface{}, arguments map[string]interface{}) {
+ userId := getOptionalString(arguments, "--userId")
+ if userId != "" {
+ setFieldValue(message, "UserId", userId)
+ }
+ anonymousId := getOptionalString(arguments, "--anonymousId")
+ if anonymousId != "" {
+ setFieldValue(message, "AnonymousId", anonymousId)
+ }
+ integrations := getOptionalString(arguments, "--integrations")
+ if integrations != "" {
+ var parsedIntegrations map[string]interface{}
+ err := json.Unmarshal([]byte(integrations), &parsedIntegrations)
+ check(err)
+ setFieldValue(message, "Integrations", parsedIntegrations)
+ }
+ context := getOptionalString(arguments, "--context")
+ if context != "" {
+ var parsedContext map[string]interface{}
+ err := json.Unmarshal([]byte(context), &parsedContext)
+ check(err)
+ setFieldValue(message, "Context", parsedContext)
+
+ }
+ timestamp := getOptionalString(arguments, "--timestamp")
+ if timestamp != "" {
+ setFieldValue(message, "Timestamp", timestamp)
+ }
+}
+
+func setFieldValue(target interface{}, field string, value interface{}) {
+ reflect.ValueOf(target).Elem().FieldByName(field).Set(reflect.ValueOf(value))
+}
+
+func getOptionalString(m map[string]interface{}, k string) string {
+ v := m[k]
+ if v == nil {
+ return ""
+ }
+ return v.(string)
+}
+
+func check(err error) {
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/vendor/github.com/segmentio/analytics-go/examples/track.go b/vendor/github.com/segmentio/analytics-go/examples/track.go
new file mode 100644
index 000000000..484c38efa
--- /dev/null
+++ b/vendor/github.com/segmentio/analytics-go/examples/track.go
@@ -0,0 +1,36 @@
+package main
+
+import "github.com/segmentio/analytics-go"
+import "time"
+
+func main() {
+ client := analytics.New("h97jamjwbh")
+ client.Interval = 30 * time.Second
+ client.Size = 100
+ client.Verbose = true
+
+ done := time.After(3 * time.Second)
+ tick := time.Tick(50 * time.Millisecond)
+
+out:
+ for {
+ select {
+ case <-done:
+ println("exiting")
+ break out
+ case <-tick:
+ client.Track(&analytics.Track{
+ Event: "Download",
+ UserId: "123456",
+ Properties: map[string]interface{}{
+ "application": "Segment Desktop",
+ "version": "1.1.0",
+ "platform": "osx",
+ },
+ })
+ }
+ }
+
+ println("flushing")
+ client.Close()
+}
diff --git a/vendor/github.com/segmentio/backo-go/README.md b/vendor/github.com/segmentio/backo-go/README.md
new file mode 100644
index 000000000..1362becf1
--- /dev/null
+++ b/vendor/github.com/segmentio/backo-go/README.md
@@ -0,0 +1,80 @@
+Backo [![GoDoc](http://godoc.org/github.com/segmentio/backo-go?status.png)](http://godoc.org/github.com/segmentio/backo-go)
+-----
+
+Exponential backoff for Go (Go port of segmentio/backo).
+
+
+Usage
+-----
+
+```go
+import "github.com/segmentio/backo-go"
+
+// Create a Backo instance.
+backo := backo.NewBacko(milliseconds(100), 2, 1, milliseconds(10*1000))
+// OR with defaults.
+backo := backo.DefaultBacko()
+
+// Use the ticker API.
+ticker := b.NewTicker()
+for {
+ timeout := time.After(5 * time.Minute)
+ select {
+ case <-ticker.C:
+ fmt.Println("ticked")
+ case <- timeout:
+ fmt.Println("timed out")
+ }
+}
+
+// Or simply work with backoff intervals directly.
+for i := 0; i < n; i++ {
+ // Sleep the current goroutine.
+ backo.Sleep(i)
+ // Retrieve the duration manually.
+ duration := backo.Duration(i)
+}
+```
+
+License
+-------
+
+```
+WWWWWW||WWWWWW
+ W W W||W W W
+ ||
+ ( OO )__________
+ / | \
+ /o o| MIT \
+ \___/||_||__||_|| *
+ || || || ||
+ _||_|| _||_||
+ (__|__|(__|__|
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Segment, Inc.
+
+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.
+```
+
+
+
+ [1]: http://github.com/segmentio/backo-java
+ [2]: http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.segment.backo&a=backo&v=LATEST \ No newline at end of file
diff --git a/vendor/github.com/segmentio/backo-go/backo.go b/vendor/github.com/segmentio/backo-go/backo.go
new file mode 100644
index 000000000..6f7b6d5e2
--- /dev/null
+++ b/vendor/github.com/segmentio/backo-go/backo.go
@@ -0,0 +1,83 @@
+package backo
+
+import (
+ "math"
+ "math/rand"
+ "time"
+)
+
+type Backo struct {
+ base time.Duration
+ factor uint8
+ jitter float64
+ cap time.Duration
+}
+
+// Creates a backo instance with the given parameters
+func NewBacko(base time.Duration, factor uint8, jitter float64, cap time.Duration) *Backo {
+ return &Backo{base, factor, jitter, cap}
+}
+
+// Creates a backo instance with the following defaults:
+// base: 100 milliseconds
+// factor: 2
+// jitter: 0
+// cap: 10 seconds
+func DefaultBacko() *Backo {
+ return NewBacko(time.Millisecond*100, 2, 0, time.Second*10)
+}
+
+// Duration returns the backoff interval for the given attempt.
+func (backo *Backo) Duration(attempt int) time.Duration {
+ duration := float64(backo.base) * math.Pow(float64(backo.factor), float64(attempt))
+
+ if backo.jitter != 0 {
+ random := rand.Float64()
+ deviation := math.Floor(random * backo.jitter * duration)
+ if (int(math.Floor(random*10)) & 1) == 0 {
+ duration = duration - deviation
+ } else {
+ duration = duration + deviation
+ }
+ }
+
+ duration = math.Min(float64(duration), float64(backo.cap))
+ return time.Duration(duration)
+}
+
+// Sleep pauses the current goroutine for the backoff interval for the given attempt.
+func (backo *Backo) Sleep(attempt int) {
+ duration := backo.Duration(attempt)
+ time.Sleep(duration)
+}
+
+type Ticker struct {
+ done chan struct{}
+ C <-chan time.Time
+}
+
+func (b *Backo) NewTicker() *Ticker {
+ c := make(chan time.Time, 1)
+ ticker := &Ticker{
+ done: make(chan struct{}, 1),
+ C: c,
+ }
+
+ go func() {
+ for i := 0; ; i++ {
+ select {
+ case t := <-time.After(b.Duration(i)):
+ c <- t
+ case <-ticker.done:
+ close(c)
+ return
+ }
+ }
+ }()
+
+ return ticker
+}
+
+func (t *Ticker) Stop() {
+ t.done <- struct{}{}
+}
diff --git a/vendor/github.com/segmentio/backo-go/backo_test.go b/vendor/github.com/segmentio/backo-go/backo_test.go
new file mode 100644
index 000000000..89933acf7
--- /dev/null
+++ b/vendor/github.com/segmentio/backo-go/backo_test.go
@@ -0,0 +1,77 @@
+package backo
+
+import (
+ "fmt"
+ "math"
+ "testing"
+ "time"
+
+ "github.com/bmizerany/assert"
+)
+
+// Tests default backo behaviour.
+func TestDefaults(t *testing.T) {
+ backo := DefaultBacko()
+
+ assert.Equal(t, milliseconds(100), backo.Duration(0))
+ assert.Equal(t, milliseconds(200), backo.Duration(1))
+ assert.Equal(t, milliseconds(400), backo.Duration(2))
+ assert.Equal(t, milliseconds(800), backo.Duration(3))
+}
+
+// Tests backo does not exceed cap.
+func TestCap(t *testing.T) {
+ backo := NewBacko(milliseconds(100), 2, 0, milliseconds(600))
+
+ assert.Equal(t, milliseconds(100), backo.Duration(0))
+ assert.Equal(t, milliseconds(200), backo.Duration(1))
+ assert.Equal(t, milliseconds(400), backo.Duration(2))
+ assert.Equal(t, milliseconds(600), backo.Duration(3))
+}
+
+// Tests that jitter adds randomness.
+func TestJitter(t *testing.T) {
+ defaultBacko := NewBacko(milliseconds(100), 2, 1, milliseconds(10*1000))
+ jitterBacko := NewBacko(milliseconds(100), 2, 1, milliseconds(10*1000))
+
+ // TODO: Check jittered durations are within a range.
+ assert.NotEqual(t, jitterBacko.Duration(0), defaultBacko.Duration(0))
+ assert.NotEqual(t, jitterBacko.Duration(1), defaultBacko.Duration(1))
+ assert.NotEqual(t, jitterBacko.Duration(2), defaultBacko.Duration(2))
+ assert.NotEqual(t, jitterBacko.Duration(3), defaultBacko.Duration(3))
+}
+
+func ExampleBacko_BackoffDefault() {
+ b := DefaultBacko()
+ ticker := b.NewTicker()
+
+ for i := 0; i < 6; i++ {
+ start := time.Now()
+ select {
+ case t := <-ticker.C:
+ fmt.Println(nearest10Millis(t.Sub(start)))
+ }
+ }
+
+ ticker.Stop()
+
+ // Output:
+ // 100
+ // 200
+ // 400
+ // 800
+ // 1600
+ // 3200
+}
+
+func nearest10Millis(d time.Duration) float64 {
+ // Typically d is something like 11 or 21, so do some magic to round the
+ // durations to the nearest 10. We divide d by 10, floor it, and multiply it
+ // by 10 again.
+ return math.Floor(float64(d/time.Millisecond/10) * 10)
+}
+
+// Returns the given milliseconds as time.Duration
+func milliseconds(ms int64) time.Duration {
+ return time.Duration(ms * 1000 * 1000)
+}
diff --git a/vendor/github.com/xtgo/uuid/AUTHORS b/vendor/github.com/xtgo/uuid/AUTHORS
new file mode 100644
index 000000000..a6f045160
--- /dev/null
+++ b/vendor/github.com/xtgo/uuid/AUTHORS
@@ -0,0 +1,5 @@
+# This source file refers to The gocql Authors for copyright purposes.
+
+Christoph Hack <christoph@tux21b.org>
+Jonathan Rudenberg <jonathan@titanous.com>
+Thorsten von Eicken <tve@rightscale.com>
diff --git a/vendor/github.com/xtgo/uuid/LICENSE b/vendor/github.com/xtgo/uuid/LICENSE
new file mode 100644
index 000000000..18d25f923
--- /dev/null
+++ b/vendor/github.com/xtgo/uuid/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2012 The gocql Authors. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+ * Neither the name of Google Inc. nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/xtgo/uuid/uuid.go b/vendor/github.com/xtgo/uuid/uuid.go
new file mode 100644
index 000000000..a0fd7a5a5
--- /dev/null
+++ b/vendor/github.com/xtgo/uuid/uuid.go
@@ -0,0 +1,204 @@
+// Copyright (c) 2012 The gocql Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package uuid can be used to generate and parse universally unique
+// identifiers, a standardized format in the form of a 128 bit number.
+//
+// http://tools.ietf.org/html/rfc4122
+package uuid
+
+import (
+ "crypto/rand"
+ "encoding/hex"
+ "errors"
+ "io"
+ "net"
+ "strconv"
+ "time"
+)
+
+type UUID [16]byte
+
+var hardwareAddr []byte
+
+const (
+ VariantNCSCompat = 0
+ VariantIETF = 2
+ VariantMicrosoft = 6
+ VariantFuture = 7
+)
+
+func init() {
+ if interfaces, err := net.Interfaces(); err == nil {
+ for _, i := range interfaces {
+ if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 {
+ hardwareAddr = i.HardwareAddr
+ break
+ }
+ }
+ }
+ if hardwareAddr == nil {
+ // If we failed to obtain the MAC address of the current computer,
+ // we will use a randomly generated 6 byte sequence instead and set
+ // the multicast bit as recommended in RFC 4122.
+ hardwareAddr = make([]byte, 6)
+ _, err := io.ReadFull(rand.Reader, hardwareAddr)
+ if err != nil {
+ panic(err)
+ }
+ hardwareAddr[0] = hardwareAddr[0] | 0x01
+ }
+}
+
+// Parse parses a 32 digit hexadecimal number (that might contain hyphens)
+// representing an UUID.
+func Parse(input string) (UUID, error) {
+ var u UUID
+ j := 0
+ for i := 0; i < len(input); i++ {
+ b := input[i]
+ switch {
+ default:
+ fallthrough
+ case j == 32:
+ goto err
+ case b == '-':
+ continue
+ case '0' <= b && b <= '9':
+ b -= '0'
+ case 'a' <= b && b <= 'f':
+ b -= 'a' - 10
+ case 'A' <= b && b <= 'F':
+ b -= 'A' - 10
+ }
+ u[j/2] |= b << byte(^j&1<<2)
+ j++
+ }
+ if j == 32 {
+ return u, nil
+ }
+err:
+ return UUID{}, errors.New("invalid UUID " + strconv.Quote(input))
+}
+
+// FromBytes converts a raw byte slice to an UUID. It will panic if the slice
+// isn't exactly 16 bytes long.
+func FromBytes(input []byte) UUID {
+ var u UUID
+ if len(input) != 16 {
+ panic("UUIDs must be exactly 16 bytes long")
+ }
+ copy(u[:], input)
+ return u
+}
+
+// NewRandom generates a totally random UUID (version 4) as described in
+// RFC 4122.
+func NewRandom() UUID {
+ var u UUID
+ io.ReadFull(rand.Reader, u[:])
+ u[6] &= 0x0F // clear version
+ u[6] |= 0x40 // set version to 4 (random uuid)
+ u[8] &= 0x3F // clear variant
+ u[8] |= 0x80 // set to IETF variant
+ return u
+}
+
+var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix()
+
+// NewTime generates a new time based UUID (version 1) as described in RFC
+// 4122. This UUID contains the MAC address of the node that generated the
+// UUID, a timestamp and a sequence number.
+func NewTime() UUID {
+ var u UUID
+
+ now := time.Now().In(time.UTC)
+ t := uint64(now.Unix()-timeBase)*10000000 + uint64(now.Nanosecond()/100)
+ u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t)
+ u[4], u[5] = byte(t>>40), byte(t>>32)
+ u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48)
+
+ var clockSeq [2]byte
+ io.ReadFull(rand.Reader, clockSeq[:])
+ u[8] = clockSeq[1]
+ u[9] = clockSeq[0]
+
+ copy(u[10:], hardwareAddr)
+
+ u[6] |= 0x10 // set version to 1 (time based uuid)
+ u[8] &= 0x3F // clear variant
+ u[8] |= 0x80 // set to IETF variant
+
+ return u
+}
+
+// String returns the UUID in it's canonical form, a 32 digit hexadecimal
+// number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
+func (u UUID) String() string {
+ buf := [36]byte{8: '-', 13: '-', 18: '-', 23: '-'}
+ hex.Encode(buf[0:], u[0:4])
+ hex.Encode(buf[9:], u[4:6])
+ hex.Encode(buf[14:], u[6:8])
+ hex.Encode(buf[19:], u[8:10])
+ hex.Encode(buf[24:], u[10:])
+ return string(buf[:])
+}
+
+// Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits
+// (16 bytes) long.
+func (u UUID) Bytes() []byte {
+ return u[:]
+}
+
+// Variant returns the variant of this UUID. This package will only generate
+// UUIDs in the IETF variant.
+func (u UUID) Variant() int {
+ x := u[8]
+ switch byte(0) {
+ case x & 0x80:
+ return VariantNCSCompat
+ case x & 0x40:
+ return VariantIETF
+ case x & 0x20:
+ return VariantMicrosoft
+ }
+ return VariantFuture
+}
+
+// Version extracts the version of this UUID variant. The RFC 4122 describes
+// five kinds of UUIDs.
+func (u UUID) Version() int {
+ return int(u[6] & 0xF0 >> 4)
+}
+
+// Node extracts the MAC address of the node who generated this UUID. It will
+// return nil if the UUID is not a time based UUID (version 1).
+func (u UUID) Node() []byte {
+ if u.Version() != 1 {
+ return nil
+ }
+ return u[10:]
+}
+
+// Timestamp extracts the timestamp information from a time based UUID
+// (version 1).
+func (u UUID) Timestamp() uint64 {
+ if u.Version() != 1 {
+ return 0
+ }
+ return uint64(u[0])<<24 + uint64(u[1])<<16 + uint64(u[2])<<8 +
+ uint64(u[3]) + uint64(u[4])<<40 + uint64(u[5])<<32 +
+ uint64(u[7])<<48 + uint64(u[6]&0x0F)<<56
+}
+
+// Time is like Timestamp, except that it returns a time.Time.
+func (u UUID) Time() time.Time {
+ t := u.Timestamp()
+ if t == 0 {
+ return time.Time{}
+ }
+ sec := t / 10000000
+ nsec := t - sec
+ return time.Unix(int64(sec)+timeBase, int64(nsec))
+}
diff --git a/vendor/github.com/xtgo/uuid/uuid_test.go b/vendor/github.com/xtgo/uuid/uuid_test.go
new file mode 100644
index 000000000..3cef4a31a
--- /dev/null
+++ b/vendor/github.com/xtgo/uuid/uuid_test.go
@@ -0,0 +1,102 @@
+// Copyright (c) 2012 The gocql Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package uuid
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestNil(t *testing.T) {
+ var uuid UUID
+ want, got := "00000000-0000-0000-0000-000000000000", uuid.String()
+ if want != got {
+ t.Fatalf("TestNil: expected %q got %q", want, got)
+ }
+}
+
+var tests = []struct {
+ input string
+ variant int
+ version int
+}{
+ {"b4f00409-cef8-4822-802c-deb20704c365", VariantIETF, 4},
+ {"f81d4fae-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1},
+ {"00000000-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1},
+ {"3051a8d7-aea7-1801-e0bf-bc539dd60cf3", VariantFuture, 1},
+ {"3051a8d7-aea7-2801-e0bf-bc539dd60cf3", VariantFuture, 2},
+ {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 3},
+ {"3051a8d7-aea7-4801-e0bf-bc539dd60cf3", VariantFuture, 4},
+ {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 5},
+ {"d0e817e1-e4b1-1801-3fe6-b4b60ccecf9d", VariantNCSCompat, 0},
+ {"d0e817e1-e4b1-1801-bfe6-b4b60ccecf9d", VariantIETF, 1},
+ {"d0e817e1-e4b1-1801-dfe6-b4b60ccecf9d", VariantMicrosoft, 0},
+ {"d0e817e1-e4b1-1801-ffe6-b4b60ccecf9d", VariantFuture, 0},
+}
+
+func TestPredefined(t *testing.T) {
+ for i := range tests {
+ uuid, err := Parse(tests[i].input)
+ if err != nil {
+ t.Errorf("Parse #%d: %v", i, err)
+ continue
+ }
+
+ if str := uuid.String(); str != tests[i].input {
+ t.Errorf("String #%d: expected %q got %q", i, tests[i].input, str)
+ continue
+ }
+
+ if variant := uuid.Variant(); variant != tests[i].variant {
+ t.Errorf("Variant #%d: expected %d got %d", i, tests[i].variant, variant)
+ }
+
+ if tests[i].variant == VariantIETF {
+ if version := uuid.Version(); version != tests[i].version {
+ t.Errorf("Version #%d: expected %d got %d", i, tests[i].version, version)
+ }
+ }
+ }
+}
+
+func TestNewRandom(t *testing.T) {
+ for i := 0; i < 20; i++ {
+ uuid := NewRandom()
+
+ if variant := uuid.Variant(); variant != VariantIETF {
+ t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant)
+ }
+ if version := uuid.Version(); version != 4 {
+ t.Errorf("wrong version. expected %d got %d", 4, version)
+ }
+ }
+}
+
+func TestNewTime(t *testing.T) {
+ var node []byte
+ timestamp := uint64(0)
+ for i := 0; i < 20; i++ {
+ uuid := NewTime()
+
+ if variant := uuid.Variant(); variant != VariantIETF {
+ t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant)
+ }
+ if version := uuid.Version(); version != 1 {
+ t.Errorf("wrong version. expected %d got %d", 1, version)
+ }
+
+ if n := uuid.Node(); !bytes.Equal(n, node) && i > 0 {
+ t.Errorf("wrong node. expected %x, got %x", node, n)
+ } else if i == 0 {
+ node = n
+ }
+
+ ts := uuid.Timestamp()
+ if ts < timestamp {
+ t.Errorf("timestamps must grow")
+ }
+ timestamp = ts
+ }
+}