[VOL-3210] Added Retry feature in BBSim for EAPOL and DHCP

Change-Id: If0586c804f906998ebc09b7770fd6a749a3a2762
diff --git a/VERSION b/VERSION
index 1610241..abd4105 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1 @@
-0.2.3-dev
-
+0.2.4
diff --git a/cmd/bbsim/bbsim.go b/cmd/bbsim/bbsim.go
index 1ca93e6..665ebd7 100644
--- a/cmd/bbsim/bbsim.go
+++ b/cmd/bbsim/bbsim.go
@@ -165,6 +165,8 @@
 		"STag":                 options.BBSim.STag,
 		"STagAllocation":       options.BBSim.STagAllocation,
 		"SadisFormat":          options.BBSim.SadisFormat,
+		"DhcpRetry":            options.BBSim.DhcpRetry,
+		"AuthRetry":            options.BBSim.AuthRetry,
 	}).Info("BroadBand Simulator is on")
 
 	// control channels, they are only closed when the goroutine needs to be terminated
diff --git a/configs/bbsim.yaml b/configs/bbsim.yaml
index c4ade64..9600659 100644
--- a/configs/bbsim.yaml
+++ b/configs/bbsim.yaml
@@ -12,6 +12,8 @@
   rest_api_address: ":50071"
   controlled_activation: "default"
   enable_perf: false
+  dhcp_retry: false
+  auth_retry: false
   # legacy_api_address: ":50072"
   # legacy_rest_api_address: ":50073"
   # sadis_rest_address: ":50074"
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 296d42f..cd0149b 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -149,6 +149,10 @@
            Set the topic on which BBSim publishes events on kafka
      -igmp
            Set this flag to start IGMP automatically
+     -dhcpRetry bool
+           Set this flag if BBSim should retry DHCP upon failure until success
+     -authRetry bool
+           Set this flag if BBSim should retry EAPOL (Authentication) upon failure until success
 
 ``BBSim`` also looks for a configuration file in ``configs/bbsim.yaml`` from
 which it reads a number of default settings. The command line options listed
diff --git a/go.mod b/go.mod
index 7784d8c..2fd2603 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,6 @@
 	github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect
 	github.com/cboling/omci v0.1.0
 	github.com/deckarep/golang-set v1.7.1 // indirect
-	github.com/fatih/structs v1.1.0
 	github.com/ghodss/yaml v1.0.0
 	github.com/golang/protobuf v1.3.2
 	github.com/google/gopacket v1.1.17
@@ -15,6 +14,7 @@
 	github.com/grpc-ecosystem/grpc-gateway v1.12.2
 	github.com/jessevdk/go-flags v1.4.0
 	github.com/jhump/protoreflect v1.5.0
+	github.com/jpillora/backoff v1.0.0
 	github.com/looplab/fsm v0.1.0
 	github.com/olekukonko/tablewriter v0.0.4
 	github.com/opencord/cordctl v0.0.0-20190909161711-01e9c1f04bf4
@@ -22,7 +22,6 @@
 	github.com/opencord/voltha-protos/v2 v2.1.2
 	github.com/pkg/errors v0.8.1 // indirect
 	github.com/sirupsen/logrus v1.4.2
-	golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135
 	google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c
 	google.golang.org/grpc v1.27.0
 	gopkg.in/yaml.v2 v2.2.8
diff --git a/go.sum b/go.sum
index e8349ed..65cdac5 100644
--- a/go.sum
+++ b/go.sum
@@ -24,8 +24,6 @@
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
-github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/frankban/quicktest v1.7.2 h1:2QxQoC1TS09S7fhCPsrvqYdvP1H5M1P1ih5ABm3BTYk=
@@ -60,6 +58,8 @@
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
 github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
+github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
+github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.9.8 h1:VMAMUUOh+gaxKTMk+zqbjsSjsIcUcL/LF4o63i82QyA=
 github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 6b879cc..78569f5 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -26,6 +26,7 @@
 
 	"github.com/cboling/omci"
 	"github.com/google/gopacket/layers"
+	backoff "github.com/jpillora/backoff"
 	"github.com/looplab/fsm"
 	"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
 	"github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
@@ -60,7 +61,7 @@
 	InternalState       *fsm.FSM
 	DiscoveryRetryDelay time.Duration // this is the time between subsequent Discovery Indication
 	DiscoveryDelay      time.Duration // this is the time to send the first Discovery Indication
-
+	Backoff             *backoff.Backoff
 	// ONU State
 	// PortNo comes with flows and it's used when sending packetIndications,
 	// There is one PortNo per UNI Port, for now we're only storing the first one
@@ -98,6 +99,13 @@
 }
 
 func CreateONU(olt *OltDevice, pon PonPort, id uint32, sTag int, cTag int, auth bool, dhcp bool, delay time.Duration, isMock bool) *Onu {
+	b := &backoff.Backoff{
+		//These are the defaults
+		Min:    1 * time.Second,
+		Max:    35 * time.Second,
+		Factor: 1.5,
+		Jitter: false,
+	}
 
 	o := Onu{
 		ID:                  0,
@@ -119,9 +127,9 @@
 		DiscoveryRetryDelay: 60 * time.Second, // this is used to send OnuDiscoveryIndications until an activate call is received
 		Flows:               []FlowKey{},
 		DiscoveryDelay:      delay,
+		Backoff:             b,
 	}
 	o.SerialNumber = o.NewSN(olt.ID, pon.ID, id)
-
 	// NOTE this state machine is used to track the operational
 	// state as requested by VOLTHA
 	o.OperState = getOperStateFSM(func(e *fsm.Event) {
@@ -397,12 +405,9 @@
 				msg, _ := message.Data.(OnuFlowUpdateMessage)
 				o.handleFlowUpdate(msg)
 			case StartEAPOL:
-				log.Infof("Receive StartEAPOL message on ONU Channel")
-				eapol.SendEapStart(o.ID, o.PonPortID, o.Sn(), o.PortNo, o.HwAddress, o.InternalState, stream)
+				o.handleEAPOLStart(stream)
 			case StartDHCP:
-				log.Infof("Receive StartDHCP message on ONU Channel")
-				// FIXME use id, ponId as SendEapStart
-				dhcp.SendDHCPDiscovery(o.PonPort.Olt.ID, o.PonPortID, o.ID, o.Sn(), o.PortNo, o.InternalState, o.HwAddress, o.CTag, stream)
+				o.handleDHCPStart(stream)
 			case OnuPacketOut:
 
 				msg, _ := message.Data.(OnuPacketMessage)
@@ -518,6 +523,38 @@
 	}
 }
 
+func (o *Onu) handleEAPOLStart(stream openolt.Openolt_EnableIndicationServer) {
+	log.Infof("Receive StartEAPOL message on ONU Channel")
+	eapol.SendEapStart(o.ID, o.PonPortID, o.Sn(), o.PortNo, o.HwAddress, o.InternalState, stream)
+	go func(delay time.Duration) {
+		time.Sleep(delay)
+		if (o.InternalState.Current() == "eap_start_sent" ||
+			o.InternalState.Current() == "eap_response_identity_sent" ||
+			o.InternalState.Current() == "eap_response_challenge_sent" ||
+			o.InternalState.Current() == "auth_failed") && common.Options.BBSim.AuthRetry {
+			o.InternalState.Event("start_auth")
+		} else if o.InternalState.Current() == "eap_response_success_received" {
+			o.Backoff.Reset()
+		}
+	}(o.Backoff.Duration())
+}
+
+func (o *Onu) handleDHCPStart(stream openolt.Openolt_EnableIndicationServer) {
+	log.Infof("Receive StartDHCP message on ONU Channel")
+	// FIXME use id, ponId as SendEapStart
+	dhcp.SendDHCPDiscovery(o.PonPort.Olt.ID, o.PonPortID, o.ID, o.Sn(), o.PortNo, o.InternalState, o.HwAddress, o.CTag, stream)
+	go func(delay time.Duration) {
+		time.Sleep(delay)
+		if (o.InternalState.Current() == "dhcp_discovery_sent" ||
+			o.InternalState.Current() == "dhcp_request_sent" ||
+			o.InternalState.Current() == "dhcp_failed") && common.Options.BBSim.DhcpRetry {
+			o.InternalState.Event("start_dhcp")
+		} else if o.InternalState.Current() == "dhcp_ack_received" {
+			o.Backoff.Reset()
+		}
+	}(o.Backoff.Duration())
+}
+
 func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
 
 	sn := new(openolt.SerialNumber)
diff --git a/internal/common/options.go b/internal/common/options.go
index b7f8bf6..6c728fb 100644
--- a/internal/common/options.go
+++ b/internal/common/options.go
@@ -121,6 +121,8 @@
 }
 
 type BBSimConfig struct {
+	DhcpRetry            bool          `yaml:"dhcp_retry"`
+	AuthRetry            bool          `yaml:"auth_retry"`
 	EnableIgmp           bool          `yaml:"enable_igmp"`
 	EnableDhcp           bool          `yaml:"enable_dhcp"`
 	EnableAuth           bool          `yaml:"enable_auth"`
@@ -187,6 +189,8 @@
 			ControlledActivation: "default",
 			EnablePerf:           false,
 			KafkaEventTopic:      "",
+			DhcpRetry:            false,
+			AuthRetry:            false,
 		},
 		OltConfig{
 			Vendor:             "BBSim",
@@ -266,6 +270,8 @@
 	enableEvents := flag.Bool("enableEvents", conf.BBSim.Events, "Enable sending BBSim events on configured kafka server")
 	kafkaAddress := flag.String("kafkaAddress", conf.BBSim.KafkaAddress, "IP:Port for kafka")
 	kafkaEventTopic := flag.String("kafkaEventTopic", conf.BBSim.KafkaEventTopic, "Ability to configure the topic on which BBSim publishes events on Kafka")
+	dhcpRetry := flag.Bool("dhcpRetry", conf.BBSim.DhcpRetry, "Set this flag if BBSim should retry DHCP upon failure until success")
+	authRetry := flag.Bool("authRetry", conf.BBSim.AuthRetry, "Set this flag if BBSim should retry EAPOL (Authentication) upon failure until success")
 	flag.Parse()
 
 	sTagAlloc, err := tagAllocationFromString(*s_tag_allocation)
@@ -311,6 +317,8 @@
 	conf.BBSim.RestApiAddress = *rest_api_address
 	conf.BBSim.SadisFormat = sf
 	conf.BBSim.KafkaEventTopic = *kafkaEventTopic
+	conf.BBSim.AuthRetry = *authRetry
+	conf.BBSim.DhcpRetry = *dhcpRetry
 
 	// update device id if not set
 	if conf.Olt.DeviceId == "" {
diff --git a/vendor/github.com/fatih/structs/.gitignore b/vendor/github.com/fatih/structs/.gitignore
deleted file mode 100644
index 8365624..0000000
--- a/vendor/github.com/fatih/structs/.gitignore
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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
-*.test
diff --git a/vendor/github.com/fatih/structs/.travis.yml b/vendor/github.com/fatih/structs/.travis.yml
deleted file mode 100644
index a08df79..0000000
--- a/vendor/github.com/fatih/structs/.travis.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-language: go
-go: 
- - 1.7.x
- - 1.8.x
- - 1.9.x
- - tip
-sudo: false
-before_install:
-- go get github.com/axw/gocov/gocov
-- go get github.com/mattn/goveralls
-- if ! go get github.com/golang/tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
-script:
-- $HOME/gopath/bin/goveralls -service=travis-ci
diff --git a/vendor/github.com/fatih/structs/README.md b/vendor/github.com/fatih/structs/README.md
deleted file mode 100644
index a75eabf..0000000
--- a/vendor/github.com/fatih/structs/README.md
+++ /dev/null
@@ -1,163 +0,0 @@
-# Structs [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structs) [![Build Status](http://img.shields.io/travis/fatih/structs.svg?style=flat-square)](https://travis-ci.org/fatih/structs) [![Coverage Status](http://img.shields.io/coveralls/fatih/structs.svg?style=flat-square)](https://coveralls.io/r/fatih/structs)
-
-Structs contains various utilities to work with Go (Golang) structs. It was
-initially used by me to convert a struct into a `map[string]interface{}`. With
-time I've added other utilities for structs.  It's basically a high level
-package based on primitives from the reflect package. Feel free to add new
-functions or improve the existing code.
-
-## Install
-
-```bash
-go get github.com/fatih/structs
-```
-
-## Usage and Examples
-
-Just like the standard lib `strings`, `bytes` and co packages, `structs` has
-many global functions to manipulate or organize your struct data. Lets define
-and declare a struct:
-
-```go
-type Server struct {
-	Name        string `json:"name,omitempty"`
-	ID          int
-	Enabled     bool
-	users       []string // not exported
-	http.Server          // embedded
-}
-
-server := &Server{
-	Name:    "gopher",
-	ID:      123456,
-	Enabled: true,
-}
-```
-
-```go
-// Convert a struct to a map[string]interface{}
-// => {"Name":"gopher", "ID":123456, "Enabled":true}
-m := structs.Map(server)
-
-// Convert the values of a struct to a []interface{}
-// => ["gopher", 123456, true]
-v := structs.Values(server)
-
-// Convert the names of a struct to a []string
-// (see "Names methods" for more info about fields)
-n := structs.Names(server)
-
-// Convert the values of a struct to a []*Field
-// (see "Field methods" for more info about fields)
-f := structs.Fields(server)
-
-// Return the struct name => "Server"
-n := structs.Name(server)
-
-// Check if any field of a struct is initialized or not.
-h := structs.HasZero(server)
-
-// Check if all fields of a struct is initialized or not.
-z := structs.IsZero(server)
-
-// Check if server is a struct or a pointer to struct
-i := structs.IsStruct(server)
-```
-
-### Struct methods
-
-The structs functions can be also used as independent methods by creating a new
-`*structs.Struct`. This is handy if you want to have more control over the
-structs (such as retrieving a single Field).
-
-```go
-// Create a new struct type:
-s := structs.New(server)
-
-m := s.Map()              // Get a map[string]interface{}
-v := s.Values()           // Get a []interface{}
-f := s.Fields()           // Get a []*Field
-n := s.Names()            // Get a []string
-f := s.Field(name)        // Get a *Field based on the given field name
-f, ok := s.FieldOk(name)  // Get a *Field based on the given field name
-n := s.Name()             // Get the struct name
-h := s.HasZero()          // Check if any field is uninitialized
-z := s.IsZero()           // Check if all fields are uninitialized
-```
-
-### Field methods
-
-We can easily examine a single Field for more detail. Below you can see how we
-get and interact with various field methods:
-
-
-```go
-s := structs.New(server)
-
-// Get the Field struct for the "Name" field
-name := s.Field("Name")
-
-// Get the underlying value,  value => "gopher"
-value := name.Value().(string)
-
-// Set the field's value
-name.Set("another gopher")
-
-// Get the field's kind, kind =>  "string"
-name.Kind()
-
-// Check if the field is exported or not
-if name.IsExported() {
-	fmt.Println("Name field is exported")
-}
-
-// Check if the value is a zero value, such as "" for string, 0 for int
-if !name.IsZero() {
-	fmt.Println("Name is initialized")
-}
-
-// Check if the field is an anonymous (embedded) field
-if !name.IsEmbedded() {
-	fmt.Println("Name is not an embedded field")
-}
-
-// Get the Field's tag value for tag name "json", tag value => "name,omitempty"
-tagValue := name.Tag("json")
-```
-
-Nested structs are supported too:
-
-```go
-addrField := s.Field("Server").Field("Addr")
-
-// Get the value for addr
-a := addrField.Value().(string)
-
-// Or get all fields
-httpServer := s.Field("Server").Fields()
-```
-
-We can also get a slice of Fields from the Struct type to iterate over all
-fields. This is handy if you wish to examine all fields:
-
-```go
-s := structs.New(server)
-
-for _, f := range s.Fields() {
-	fmt.Printf("field name: %+v\n", f.Name())
-
-	if f.IsExported() {
-		fmt.Printf("value   : %+v\n", f.Value())
-		fmt.Printf("is zero : %+v\n", f.IsZero())
-	}
-}
-```
-
-## Credits
-
- * [Fatih Arslan](https://github.com/fatih)
- * [Cihangir Savas](https://github.com/cihangir)
-
-## License
-
-The MIT License (MIT) - see LICENSE.md for more details
diff --git a/vendor/github.com/fatih/structs/field.go b/vendor/github.com/fatih/structs/field.go
deleted file mode 100644
index e697832..0000000
--- a/vendor/github.com/fatih/structs/field.go
+++ /dev/null
@@ -1,141 +0,0 @@
-package structs
-
-import (
-	"errors"
-	"fmt"
-	"reflect"
-)
-
-var (
-	errNotExported = errors.New("field is not exported")
-	errNotSettable = errors.New("field is not settable")
-)
-
-// Field represents a single struct field that encapsulates high level
-// functions around the field.
-type Field struct {
-	value      reflect.Value
-	field      reflect.StructField
-	defaultTag string
-}
-
-// Tag returns the value associated with key in the tag string. If there is no
-// such key in the tag, Tag returns the empty string.
-func (f *Field) Tag(key string) string {
-	return f.field.Tag.Get(key)
-}
-
-// Value returns the underlying value of the field. It panics if the field
-// is not exported.
-func (f *Field) Value() interface{} {
-	return f.value.Interface()
-}
-
-// IsEmbedded returns true if the given field is an anonymous field (embedded)
-func (f *Field) IsEmbedded() bool {
-	return f.field.Anonymous
-}
-
-// IsExported returns true if the given field is exported.
-func (f *Field) IsExported() bool {
-	return f.field.PkgPath == ""
-}
-
-// IsZero returns true if the given field is not initialized (has a zero value).
-// It panics if the field is not exported.
-func (f *Field) IsZero() bool {
-	zero := reflect.Zero(f.value.Type()).Interface()
-	current := f.Value()
-
-	return reflect.DeepEqual(current, zero)
-}
-
-// Name returns the name of the given field
-func (f *Field) Name() string {
-	return f.field.Name
-}
-
-// Kind returns the fields kind, such as "string", "map", "bool", etc ..
-func (f *Field) Kind() reflect.Kind {
-	return f.value.Kind()
-}
-
-// Set sets the field to given value v. It returns an error if the field is not
-// settable (not addressable or not exported) or if the given value's type
-// doesn't match the fields type.
-func (f *Field) Set(val interface{}) error {
-	// we can't set unexported fields, so be sure this field is exported
-	if !f.IsExported() {
-		return errNotExported
-	}
-
-	// do we get here? not sure...
-	if !f.value.CanSet() {
-		return errNotSettable
-	}
-
-	given := reflect.ValueOf(val)
-
-	if f.value.Kind() != given.Kind() {
-		return fmt.Errorf("wrong kind. got: %s want: %s", given.Kind(), f.value.Kind())
-	}
-
-	f.value.Set(given)
-	return nil
-}
-
-// Zero sets the field to its zero value. It returns an error if the field is not
-// settable (not addressable or not exported).
-func (f *Field) Zero() error {
-	zero := reflect.Zero(f.value.Type()).Interface()
-	return f.Set(zero)
-}
-
-// Fields returns a slice of Fields. This is particular handy to get the fields
-// of a nested struct . A struct tag with the content of "-" ignores the
-// checking of that particular field. Example:
-//
-//   // Field is ignored by this package.
-//   Field *http.Request `structs:"-"`
-//
-// It panics if field is not exported or if field's kind is not struct
-func (f *Field) Fields() []*Field {
-	return getFields(f.value, f.defaultTag)
-}
-
-// Field returns the field from a nested struct. It panics if the nested struct
-// is not exported or if the field was not found.
-func (f *Field) Field(name string) *Field {
-	field, ok := f.FieldOk(name)
-	if !ok {
-		panic("field not found")
-	}
-
-	return field
-}
-
-// FieldOk returns the field from a nested struct. The boolean returns whether
-// the field was found (true) or not (false).
-func (f *Field) FieldOk(name string) (*Field, bool) {
-	value := &f.value
-	// value must be settable so we need to make sure it holds the address of the
-	// variable and not a copy, so we can pass the pointer to strctVal instead of a
-	// copy (which is not assigned to any variable, hence not settable).
-	// see "https://blog.golang.org/laws-of-reflection#TOC_8."
-	if f.value.Kind() != reflect.Ptr {
-		a := f.value.Addr()
-		value = &a
-	}
-	v := strctVal(value.Interface())
-	t := v.Type()
-
-	field, ok := t.FieldByName(name)
-	if !ok {
-		return nil, false
-	}
-
-	return &Field{
-		field: field,
-		value: v.FieldByName(name),
-	}, true
-}
diff --git a/vendor/github.com/fatih/structs/structs.go b/vendor/github.com/fatih/structs/structs.go
deleted file mode 100644
index 3a87706..0000000
--- a/vendor/github.com/fatih/structs/structs.go
+++ /dev/null
@@ -1,584 +0,0 @@
-// Package structs contains various utilities functions to work with structs.
-package structs
-
-import (
-	"fmt"
-
-	"reflect"
-)
-
-var (
-	// DefaultTagName is the default tag name for struct fields which provides
-	// a more granular to tweak certain structs. Lookup the necessary functions
-	// for more info.
-	DefaultTagName = "structs" // struct's field default tag name
-)
-
-// Struct encapsulates a struct type to provide several high level functions
-// around the struct.
-type Struct struct {
-	raw     interface{}
-	value   reflect.Value
-	TagName string
-}
-
-// New returns a new *Struct with the struct s. It panics if the s's kind is
-// not struct.
-func New(s interface{}) *Struct {
-	return &Struct{
-		raw:     s,
-		value:   strctVal(s),
-		TagName: DefaultTagName,
-	}
-}
-
-// Map converts the given struct to a map[string]interface{}, where the keys
-// of the map are the field names and the values of the map the associated
-// values of the fields. The default key string is the struct field name but
-// can be changed in the struct field's tag value. The "structs" key in the
-// struct's field tag value is the key name. Example:
-//
-//   // Field appears in map as key "myName".
-//   Name string `structs:"myName"`
-//
-// A tag value with the content of "-" ignores that particular field. Example:
-//
-//   // Field is ignored by this package.
-//   Field bool `structs:"-"`
-//
-// A tag value with the content of "string" uses the stringer to get the value. Example:
-//
-//   // The value will be output of Animal's String() func.
-//   // Map will panic if Animal does not implement String().
-//   Field *Animal `structs:"field,string"`
-//
-// A tag value with the option of "flatten" used in a struct field is to flatten its fields
-// in the output map. Example:
-//
-//   // The FieldStruct's fields will be flattened into the output map.
-//   FieldStruct time.Time `structs:",flatten"`
-//
-// A tag value with the option of "omitnested" stops iterating further if the type
-// is a struct. Example:
-//
-//   // Field is not processed further by this package.
-//   Field time.Time     `structs:"myName,omitnested"`
-//   Field *http.Request `structs:",omitnested"`
-//
-// A tag value with the option of "omitempty" ignores that particular field if
-// the field value is empty. Example:
-//
-//   // Field appears in map as key "myName", but the field is
-//   // skipped if empty.
-//   Field string `structs:"myName,omitempty"`
-//
-//   // Field appears in map as key "Field" (the default), but
-//   // the field is skipped if empty.
-//   Field string `structs:",omitempty"`
-//
-// Note that only exported fields of a struct can be accessed, non exported
-// fields will be neglected.
-func (s *Struct) Map() map[string]interface{} {
-	out := make(map[string]interface{})
-	s.FillMap(out)
-	return out
-}
-
-// FillMap is the same as Map. Instead of returning the output, it fills the
-// given map.
-func (s *Struct) FillMap(out map[string]interface{}) {
-	if out == nil {
-		return
-	}
-
-	fields := s.structFields()
-
-	for _, field := range fields {
-		name := field.Name
-		val := s.value.FieldByName(name)
-		isSubStruct := false
-		var finalVal interface{}
-
-		tagName, tagOpts := parseTag(field.Tag.Get(s.TagName))
-		if tagName != "" {
-			name = tagName
-		}
-
-		// if the value is a zero value and the field is marked as omitempty do
-		// not include
-		if tagOpts.Has("omitempty") {
-			zero := reflect.Zero(val.Type()).Interface()
-			current := val.Interface()
-
-			if reflect.DeepEqual(current, zero) {
-				continue
-			}
-		}
-
-		if !tagOpts.Has("omitnested") {
-			finalVal = s.nested(val)
-
-			v := reflect.ValueOf(val.Interface())
-			if v.Kind() == reflect.Ptr {
-				v = v.Elem()
-			}
-
-			switch v.Kind() {
-			case reflect.Map, reflect.Struct:
-				isSubStruct = true
-			}
-		} else {
-			finalVal = val.Interface()
-		}
-
-		if tagOpts.Has("string") {
-			s, ok := val.Interface().(fmt.Stringer)
-			if ok {
-				out[name] = s.String()
-			}
-			continue
-		}
-
-		if isSubStruct && (tagOpts.Has("flatten")) {
-			for k := range finalVal.(map[string]interface{}) {
-				out[k] = finalVal.(map[string]interface{})[k]
-			}
-		} else {
-			out[name] = finalVal
-		}
-	}
-}
-
-// Values converts the given s struct's field values to a []interface{}.  A
-// struct tag with the content of "-" ignores the that particular field.
-// Example:
-//
-//   // Field is ignored by this package.
-//   Field int `structs:"-"`
-//
-// A value with the option of "omitnested" stops iterating further if the type
-// is a struct. Example:
-//
-//   // Fields is not processed further by this package.
-//   Field time.Time     `structs:",omitnested"`
-//   Field *http.Request `structs:",omitnested"`
-//
-// A tag value with the option of "omitempty" ignores that particular field and
-// is not added to the values if the field value is empty. Example:
-//
-//   // Field is skipped if empty
-//   Field string `structs:",omitempty"`
-//
-// Note that only exported fields of a struct can be accessed, non exported
-// fields  will be neglected.
-func (s *Struct) Values() []interface{} {
-	fields := s.structFields()
-
-	var t []interface{}
-
-	for _, field := range fields {
-		val := s.value.FieldByName(field.Name)
-
-		_, tagOpts := parseTag(field.Tag.Get(s.TagName))
-
-		// if the value is a zero value and the field is marked as omitempty do
-		// not include
-		if tagOpts.Has("omitempty") {
-			zero := reflect.Zero(val.Type()).Interface()
-			current := val.Interface()
-
-			if reflect.DeepEqual(current, zero) {
-				continue
-			}
-		}
-
-		if tagOpts.Has("string") {
-			s, ok := val.Interface().(fmt.Stringer)
-			if ok {
-				t = append(t, s.String())
-			}
-			continue
-		}
-
-		if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
-			// look out for embedded structs, and convert them to a
-			// []interface{} to be added to the final values slice
-			t = append(t, Values(val.Interface())...)
-		} else {
-			t = append(t, val.Interface())
-		}
-	}
-
-	return t
-}
-
-// Fields returns a slice of Fields. A struct tag with the content of "-"
-// ignores the checking of that particular field. Example:
-//
-//   // Field is ignored by this package.
-//   Field bool `structs:"-"`
-//
-// It panics if s's kind is not struct.
-func (s *Struct) Fields() []*Field {
-	return getFields(s.value, s.TagName)
-}
-
-// Names returns a slice of field names. A struct tag with the content of "-"
-// ignores the checking of that particular field. Example:
-//
-//   // Field is ignored by this package.
-//   Field bool `structs:"-"`
-//
-// It panics if s's kind is not struct.
-func (s *Struct) Names() []string {
-	fields := getFields(s.value, s.TagName)
-
-	names := make([]string, len(fields))
-
-	for i, field := range fields {
-		names[i] = field.Name()
-	}
-
-	return names
-}
-
-func getFields(v reflect.Value, tagName string) []*Field {
-	if v.Kind() == reflect.Ptr {
-		v = v.Elem()
-	}
-
-	t := v.Type()
-
-	var fields []*Field
-
-	for i := 0; i < t.NumField(); i++ {
-		field := t.Field(i)
-
-		if tag := field.Tag.Get(tagName); tag == "-" {
-			continue
-		}
-
-		f := &Field{
-			field: field,
-			value: v.FieldByName(field.Name),
-		}
-
-		fields = append(fields, f)
-
-	}
-
-	return fields
-}
-
-// Field returns a new Field struct that provides several high level functions
-// around a single struct field entity. It panics if the field is not found.
-func (s *Struct) Field(name string) *Field {
-	f, ok := s.FieldOk(name)
-	if !ok {
-		panic("field not found")
-	}
-
-	return f
-}
-
-// FieldOk returns a new Field struct that provides several high level functions
-// around a single struct field entity. The boolean returns true if the field
-// was found.
-func (s *Struct) FieldOk(name string) (*Field, bool) {
-	t := s.value.Type()
-
-	field, ok := t.FieldByName(name)
-	if !ok {
-		return nil, false
-	}
-
-	return &Field{
-		field:      field,
-		value:      s.value.FieldByName(name),
-		defaultTag: s.TagName,
-	}, true
-}
-
-// IsZero returns true if all fields in a struct is a zero value (not
-// initialized) A struct tag with the content of "-" ignores the checking of
-// that particular field. Example:
-//
-//   // Field is ignored by this package.
-//   Field bool `structs:"-"`
-//
-// A value with the option of "omitnested" stops iterating further if the type
-// is a struct. Example:
-//
-//   // Field is not processed further by this package.
-//   Field time.Time     `structs:"myName,omitnested"`
-//   Field *http.Request `structs:",omitnested"`
-//
-// Note that only exported fields of a struct can be accessed, non exported
-// fields  will be neglected. It panics if s's kind is not struct.
-func (s *Struct) IsZero() bool {
-	fields := s.structFields()
-
-	for _, field := range fields {
-		val := s.value.FieldByName(field.Name)
-
-		_, tagOpts := parseTag(field.Tag.Get(s.TagName))
-
-		if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
-			ok := IsZero(val.Interface())
-			if !ok {
-				return false
-			}
-
-			continue
-		}
-
-		// zero value of the given field, such as "" for string, 0 for int
-		zero := reflect.Zero(val.Type()).Interface()
-
-		//  current value of the given field
-		current := val.Interface()
-
-		if !reflect.DeepEqual(current, zero) {
-			return false
-		}
-	}
-
-	return true
-}
-
-// HasZero returns true if a field in a struct is not initialized (zero value).
-// A struct tag with the content of "-" ignores the checking of that particular
-// field. Example:
-//
-//   // Field is ignored by this package.
-//   Field bool `structs:"-"`
-//
-// A value with the option of "omitnested" stops iterating further if the type
-// is a struct. Example:
-//
-//   // Field is not processed further by this package.
-//   Field time.Time     `structs:"myName,omitnested"`
-//   Field *http.Request `structs:",omitnested"`
-//
-// Note that only exported fields of a struct can be accessed, non exported
-// fields  will be neglected. It panics if s's kind is not struct.
-func (s *Struct) HasZero() bool {
-	fields := s.structFields()
-
-	for _, field := range fields {
-		val := s.value.FieldByName(field.Name)
-
-		_, tagOpts := parseTag(field.Tag.Get(s.TagName))
-
-		if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") {
-			ok := HasZero(val.Interface())
-			if ok {
-				return true
-			}
-
-			continue
-		}
-
-		// zero value of the given field, such as "" for string, 0 for int
-		zero := reflect.Zero(val.Type()).Interface()
-
-		//  current value of the given field
-		current := val.Interface()
-
-		if reflect.DeepEqual(current, zero) {
-			return true
-		}
-	}
-
-	return false
-}
-
-// Name returns the structs's type name within its package. For more info refer
-// to Name() function.
-func (s *Struct) Name() string {
-	return s.value.Type().Name()
-}
-
-// structFields returns the exported struct fields for a given s struct. This
-// is a convenient helper method to avoid duplicate code in some of the
-// functions.
-func (s *Struct) structFields() []reflect.StructField {
-	t := s.value.Type()
-
-	var f []reflect.StructField
-
-	for i := 0; i < t.NumField(); i++ {
-		field := t.Field(i)
-		// we can't access the value of unexported fields
-		if field.PkgPath != "" {
-			continue
-		}
-
-		// don't check if it's omitted
-		if tag := field.Tag.Get(s.TagName); tag == "-" {
-			continue
-		}
-
-		f = append(f, field)
-	}
-
-	return f
-}
-
-func strctVal(s interface{}) reflect.Value {
-	v := reflect.ValueOf(s)
-
-	// if pointer get the underlying element≤
-	for v.Kind() == reflect.Ptr {
-		v = v.Elem()
-	}
-
-	if v.Kind() != reflect.Struct {
-		panic("not struct")
-	}
-
-	return v
-}
-
-// Map converts the given struct to a map[string]interface{}. For more info
-// refer to Struct types Map() method. It panics if s's kind is not struct.
-func Map(s interface{}) map[string]interface{} {
-	return New(s).Map()
-}
-
-// FillMap is the same as Map. Instead of returning the output, it fills the
-// given map.
-func FillMap(s interface{}, out map[string]interface{}) {
-	New(s).FillMap(out)
-}
-
-// Values converts the given struct to a []interface{}. For more info refer to
-// Struct types Values() method.  It panics if s's kind is not struct.
-func Values(s interface{}) []interface{} {
-	return New(s).Values()
-}
-
-// Fields returns a slice of *Field. For more info refer to Struct types
-// Fields() method.  It panics if s's kind is not struct.
-func Fields(s interface{}) []*Field {
-	return New(s).Fields()
-}
-
-// Names returns a slice of field names. For more info refer to Struct types
-// Names() method.  It panics if s's kind is not struct.
-func Names(s interface{}) []string {
-	return New(s).Names()
-}
-
-// IsZero returns true if all fields is equal to a zero value. For more info
-// refer to Struct types IsZero() method.  It panics if s's kind is not struct.
-func IsZero(s interface{}) bool {
-	return New(s).IsZero()
-}
-
-// HasZero returns true if any field is equal to a zero value. For more info
-// refer to Struct types HasZero() method.  It panics if s's kind is not struct.
-func HasZero(s interface{}) bool {
-	return New(s).HasZero()
-}
-
-// IsStruct returns true if the given variable is a struct or a pointer to
-// struct.
-func IsStruct(s interface{}) bool {
-	v := reflect.ValueOf(s)
-	if v.Kind() == reflect.Ptr {
-		v = v.Elem()
-	}
-
-	// uninitialized zero value of a struct
-	if v.Kind() == reflect.Invalid {
-		return false
-	}
-
-	return v.Kind() == reflect.Struct
-}
-
-// Name returns the structs's type name within its package. It returns an
-// empty string for unnamed types. It panics if s's kind is not struct.
-func Name(s interface{}) string {
-	return New(s).Name()
-}
-
-// nested retrieves recursively all types for the given value and returns the
-// nested value.
-func (s *Struct) nested(val reflect.Value) interface{} {
-	var finalVal interface{}
-
-	v := reflect.ValueOf(val.Interface())
-	if v.Kind() == reflect.Ptr {
-		v = v.Elem()
-	}
-
-	switch v.Kind() {
-	case reflect.Struct:
-		n := New(val.Interface())
-		n.TagName = s.TagName
-		m := n.Map()
-
-		// do not add the converted value if there are no exported fields, ie:
-		// time.Time
-		if len(m) == 0 {
-			finalVal = val.Interface()
-		} else {
-			finalVal = m
-		}
-	case reflect.Map:
-		// get the element type of the map
-		mapElem := val.Type()
-		switch val.Type().Kind() {
-		case reflect.Ptr, reflect.Array, reflect.Map,
-			reflect.Slice, reflect.Chan:
-			mapElem = val.Type().Elem()
-			if mapElem.Kind() == reflect.Ptr {
-				mapElem = mapElem.Elem()
-			}
-		}
-
-		// only iterate over struct types, ie: map[string]StructType,
-		// map[string][]StructType,
-		if mapElem.Kind() == reflect.Struct ||
-			(mapElem.Kind() == reflect.Slice &&
-				mapElem.Elem().Kind() == reflect.Struct) {
-			m := make(map[string]interface{}, val.Len())
-			for _, k := range val.MapKeys() {
-				m[k.String()] = s.nested(val.MapIndex(k))
-			}
-			finalVal = m
-			break
-		}
-
-		// TODO(arslan): should this be optional?
-		finalVal = val.Interface()
-	case reflect.Slice, reflect.Array:
-		if val.Type().Kind() == reflect.Interface {
-			finalVal = val.Interface()
-			break
-		}
-
-		// TODO(arslan): should this be optional?
-		// do not iterate of non struct types, just pass the value. Ie: []int,
-		// []string, co... We only iterate further if it's a struct.
-		// i.e []foo or []*foo
-		if val.Type().Elem().Kind() != reflect.Struct &&
-			!(val.Type().Elem().Kind() == reflect.Ptr &&
-				val.Type().Elem().Elem().Kind() == reflect.Struct) {
-			finalVal = val.Interface()
-			break
-		}
-
-		slices := make([]interface{}, val.Len())
-		for x := 0; x < val.Len(); x++ {
-			slices[x] = s.nested(val.Index(x))
-		}
-		finalVal = slices
-	default:
-		finalVal = val.Interface()
-	}
-
-	return finalVal
-}
diff --git a/vendor/github.com/fatih/structs/tags.go b/vendor/github.com/fatih/structs/tags.go
deleted file mode 100644
index 136a31e..0000000
--- a/vendor/github.com/fatih/structs/tags.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package structs
-
-import "strings"
-
-// tagOptions contains a slice of tag options
-type tagOptions []string
-
-// Has returns true if the given option is available in tagOptions
-func (t tagOptions) Has(opt string) bool {
-	for _, tagOpt := range t {
-		if tagOpt == opt {
-			return true
-		}
-	}
-
-	return false
-}
-
-// parseTag splits a struct field's tag into its name and a list of options
-// which comes after a name. A tag is in the form of: "name,option1,option2".
-// The name can be neglectected.
-func parseTag(tag string) (string, tagOptions) {
-	// tag is one of followings:
-	// ""
-	// "name"
-	// "name,opt"
-	// "name,opt,opt2"
-	// ",opt"
-
-	res := strings.Split(tag, ",")
-	return res[0], res[1:]
-}
diff --git a/vendor/github.com/fatih/structs/LICENSE b/vendor/github.com/jpillora/backoff/LICENSE
similarity index 96%
rename from vendor/github.com/fatih/structs/LICENSE
rename to vendor/github.com/jpillora/backoff/LICENSE
index 34504e4..1cc7080 100644
--- a/vendor/github.com/fatih/structs/LICENSE
+++ b/vendor/github.com/jpillora/backoff/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2014 Fatih Arslan
+Copyright (c) 2017 Jaime Pillora
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -18,4 +18,4 @@
 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.
\ No newline at end of file
+SOFTWARE.
diff --git a/vendor/github.com/jpillora/backoff/README.md b/vendor/github.com/jpillora/backoff/README.md
new file mode 100644
index 0000000..ee4d623
--- /dev/null
+++ b/vendor/github.com/jpillora/backoff/README.md
@@ -0,0 +1,119 @@
+# Backoff
+
+A simple exponential backoff counter in Go (Golang)
+
+[![GoDoc](https://godoc.org/github.com/jpillora/backoff?status.svg)](https://godoc.org/github.com/jpillora/backoff) [![Circle CI](https://circleci.com/gh/jpillora/backoff.svg?style=shield)](https://circleci.com/gh/jpillora/backoff)
+
+### Install
+
+```
+$ go get -v github.com/jpillora/backoff
+```
+
+### Usage
+
+Backoff is a `time.Duration` counter. It starts at `Min`. After every call to `Duration()` it is  multiplied by `Factor`. It is capped at `Max`. It returns to `Min` on every call to `Reset()`. `Jitter` adds randomness ([see below](#example-using-jitter)). Used in conjunction with the `time` package.
+
+---
+
+#### Simple example
+
+``` go
+
+b := &backoff.Backoff{
+	//These are the defaults
+	Min:    100 * time.Millisecond,
+	Max:    10 * time.Second,
+	Factor: 2,
+	Jitter: false,
+}
+
+fmt.Printf("%s\n", b.Duration())
+fmt.Printf("%s\n", b.Duration())
+fmt.Printf("%s\n", b.Duration())
+
+fmt.Printf("Reset!\n")
+b.Reset()
+
+fmt.Printf("%s\n", b.Duration())
+```
+
+```
+100ms
+200ms
+400ms
+Reset!
+100ms
+```
+
+---
+
+#### Example using `net` package
+
+``` go
+b := &backoff.Backoff{
+    Max:    5 * time.Minute,
+}
+
+for {
+	conn, err := net.Dial("tcp", "example.com:5309")
+	if err != nil {
+		d := b.Duration()
+		fmt.Printf("%s, reconnecting in %s", err, d)
+		time.Sleep(d)
+		continue
+	}
+	//connected
+	b.Reset()
+	conn.Write([]byte("hello world!"))
+	// ... Read ... Write ... etc
+	conn.Close()
+	//disconnected
+}
+
+```
+
+---
+
+#### Example using `Jitter`
+
+Enabling `Jitter` adds some randomization to the backoff durations. [See Amazon's writeup of performance gains using jitter](http://www.awsarchitectureblog.com/2015/03/backoff.html). Seeding is not necessary but doing so gives repeatable results.
+
+```go
+import "math/rand"
+
+b := &backoff.Backoff{
+	Jitter: true,
+}
+
+rand.Seed(42)
+
+fmt.Printf("%s\n", b.Duration())
+fmt.Printf("%s\n", b.Duration())
+fmt.Printf("%s\n", b.Duration())
+
+fmt.Printf("Reset!\n")
+b.Reset()
+
+fmt.Printf("%s\n", b.Duration())
+fmt.Printf("%s\n", b.Duration())
+fmt.Printf("%s\n", b.Duration())
+```
+
+```
+100ms
+106.600049ms
+281.228155ms
+Reset!
+100ms
+104.381845ms
+214.957989ms
+```
+
+#### Documentation
+
+https://godoc.org/github.com/jpillora/backoff
+
+#### Credits
+
+Forked from [some JavaScript](https://github.com/segmentio/backo) written by [@tj](https://github.com/tj)
diff --git a/vendor/github.com/jpillora/backoff/backoff.go b/vendor/github.com/jpillora/backoff/backoff.go
new file mode 100644
index 0000000..d113e68
--- /dev/null
+++ b/vendor/github.com/jpillora/backoff/backoff.go
@@ -0,0 +1,100 @@
+// Package backoff provides an exponential-backoff implementation.
+package backoff
+
+import (
+	"math"
+	"math/rand"
+	"sync/atomic"
+	"time"
+)
+
+// Backoff is a time.Duration counter, starting at Min. After every call to
+// the Duration method the current timing is multiplied by Factor, but it
+// never exceeds Max.
+//
+// Backoff is not generally concurrent-safe, but the ForAttempt method can
+// be used concurrently.
+type Backoff struct {
+	attempt uint64
+	// Factor is the multiplying factor for each increment step
+	Factor float64
+	// Jitter eases contention by randomizing backoff steps
+	Jitter bool
+	// Min and Max are the minimum and maximum values of the counter
+	Min, Max time.Duration
+}
+
+// Duration returns the duration for the current attempt before incrementing
+// the attempt counter. See ForAttempt.
+func (b *Backoff) Duration() time.Duration {
+	d := b.ForAttempt(float64(atomic.AddUint64(&b.attempt, 1) - 1))
+	return d
+}
+
+const maxInt64 = float64(math.MaxInt64 - 512)
+
+// ForAttempt returns the duration for a specific attempt. This is useful if
+// you have a large number of independent Backoffs, but don't want use
+// unnecessary memory storing the Backoff parameters per Backoff. The first
+// attempt should be 0.
+//
+// ForAttempt is concurrent-safe.
+func (b *Backoff) ForAttempt(attempt float64) time.Duration {
+	// Zero-values are nonsensical, so we use
+	// them to apply defaults
+	min := b.Min
+	if min <= 0 {
+		min = 100 * time.Millisecond
+	}
+	max := b.Max
+	if max <= 0 {
+		max = 10 * time.Second
+	}
+	if min >= max {
+		// short-circuit
+		return max
+	}
+	factor := b.Factor
+	if factor <= 0 {
+		factor = 2
+	}
+	//calculate this duration
+	minf := float64(min)
+	durf := minf * math.Pow(factor, attempt)
+	if b.Jitter {
+		durf = rand.Float64()*(durf-minf) + minf
+	}
+	//ensure float64 wont overflow int64
+	if durf > maxInt64 {
+		return max
+	}
+	dur := time.Duration(durf)
+	//keep within bounds
+	if dur < min {
+		return min
+	}
+	if dur > max {
+		return max
+	}
+	return dur
+}
+
+// Reset restarts the current attempt counter at zero.
+func (b *Backoff) Reset() {
+	atomic.StoreUint64(&b.attempt, 0)
+}
+
+// Attempt returns the current attempt counter value.
+func (b *Backoff) Attempt() float64 {
+	return float64(atomic.LoadUint64(&b.attempt))
+}
+
+// Copy returns a backoff with equals constraints as the original
+func (b *Backoff) Copy() *Backoff {
+	return &Backoff{
+		Factor: b.Factor,
+		Jitter: b.Jitter,
+		Min:    b.Min,
+		Max:    b.Max,
+	}
+}
diff --git a/vendor/github.com/jpillora/backoff/go.mod b/vendor/github.com/jpillora/backoff/go.mod
new file mode 100644
index 0000000..7c41bc6
--- /dev/null
+++ b/vendor/github.com/jpillora/backoff/go.mod
@@ -0,0 +1,3 @@
+module github.com/jpillora/backoff
+
+go 1.13
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 69bbb56..6e2ad93 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -16,8 +16,6 @@
 github.com/eapache/go-xerial-snappy
 # github.com/eapache/queue v1.1.0
 github.com/eapache/queue
-# github.com/fatih/structs v1.1.0
-github.com/fatih/structs
 # github.com/ghodss/yaml v1.0.0
 github.com/ghodss/yaml
 # github.com/golang/protobuf v1.3.2
@@ -64,6 +62,8 @@
 github.com/jhump/protoreflect/desc/internal
 github.com/jhump/protoreflect/dynamic
 github.com/jhump/protoreflect/internal
+# github.com/jpillora/backoff v1.0.0
+github.com/jpillora/backoff
 # github.com/klauspost/compress v1.9.8
 github.com/klauspost/compress/fse
 github.com/klauspost/compress/huff0