Browse Source

Merge pull request #16229 from icecrime/discovery

Add builtin nodes discovery
Andrea Luzzardi 9 years ago
parent
commit
9324b154d2
48 changed files with 6933 additions and 6 deletions
  1. 3 0
      api/client/info.go
  2. 1 0
      api/types/types.go
  3. 12 1
      daemon/config.go
  4. 0 1
      daemon/config_experimental.go
  5. 12 0
      daemon/daemon.go
  6. 2 2
      daemon/daemon_unix.go
  7. 48 0
      daemon/discovery.go
  8. 1 0
      daemon/info.go
  9. 1 0
      docs/reference/api/docker_remote_api_v1.21.md
  10. 8 0
      docs/reference/commandline/daemon.md
  11. 2 2
      experimental/network_overlay.md
  12. 2 0
      hack/.vendor-helpers.sh
  13. 2 0
      hack/vendor.sh
  14. 19 0
      integration-cli/docker_cli_info_test.go
  15. 41 0
      pkg/discovery/README.md
  16. 53 0
      pkg/discovery/backends.go
  17. 35 0
      pkg/discovery/discovery.go
  18. 120 0
      pkg/discovery/discovery_test.go
  19. 97 0
      pkg/discovery/entry.go
  20. 109 0
      pkg/discovery/file/file.go
  21. 106 0
      pkg/discovery/file/file_test.go
  22. 35 0
      pkg/discovery/generator.go
  23. 55 0
      pkg/discovery/generator_test.go
  24. 148 0
      pkg/discovery/kv/kv.go
  25. 119 0
      pkg/discovery/kv/kv_test.go
  26. 54 0
      pkg/discovery/nodes/nodes.go
  27. 43 0
      pkg/discovery/nodes/nodes_test.go
  28. 113 0
      vendor/src/github.com/docker/libkv/store/mock/mock.go
  29. 22 0
      vendor/src/github.com/stretchr/objx/.gitignore
  30. 23 0
      vendor/src/github.com/stretchr/objx/LICENSE.md
  31. 3 0
      vendor/src/github.com/stretchr/objx/README.md
  32. 179 0
      vendor/src/github.com/stretchr/objx/accessors.go
  33. 13 0
      vendor/src/github.com/stretchr/objx/constants.go
  34. 117 0
      vendor/src/github.com/stretchr/objx/conversions.go
  35. 72 0
      vendor/src/github.com/stretchr/objx/doc.go
  36. 222 0
      vendor/src/github.com/stretchr/objx/map.go
  37. 81 0
      vendor/src/github.com/stretchr/objx/mutations.go
  38. 14 0
      vendor/src/github.com/stretchr/objx/security.go
  39. 17 0
      vendor/src/github.com/stretchr/objx/tests.go
  40. 2881 0
      vendor/src/github.com/stretchr/objx/type_specific_codegen.go
  41. 13 0
      vendor/src/github.com/stretchr/objx/value.go
  42. 911 0
      vendor/src/github.com/stretchr/testify/assert/assertions.go
  43. 45 0
      vendor/src/github.com/stretchr/testify/assert/doc.go
  44. 10 0
      vendor/src/github.com/stretchr/testify/assert/errors.go
  45. 275 0
      vendor/src/github.com/stretchr/testify/assert/forward_assertions.go
  46. 157 0
      vendor/src/github.com/stretchr/testify/assert/http_assertions.go
  47. 43 0
      vendor/src/github.com/stretchr/testify/mock/doc.go
  48. 594 0
      vendor/src/github.com/stretchr/testify/mock/mock.go

+ 3 - 0
api/client/info.go

@@ -103,6 +103,9 @@ func (cli *DockerCli) CmdInfo(args ...string) error {
 	}
 	}
 
 
 	ioutils.FprintfIfTrue(cli.out, "Experimental: %v\n", info.ExperimentalBuild)
 	ioutils.FprintfIfTrue(cli.out, "Experimental: %v\n", info.ExperimentalBuild)
+	if info.ClusterStore != "" {
+		fmt.Fprintf(cli.out, "Cluster store: %s\n", info.ClusterStore)
+	}
 
 
 	return nil
 	return nil
 }
 }

+ 1 - 0
api/types/types.go

@@ -216,6 +216,7 @@ type Info struct {
 	Labels             []string
 	Labels             []string
 	ExperimentalBuild  bool
 	ExperimentalBuild  bool
 	ServerVersion      string
 	ServerVersion      string
+	ClusterStore       string
 }
 }
 
 
 // ExecStartCheck is a temp struct used by execStart
 // ExecStartCheck is a temp struct used by execStart

+ 12 - 1
daemon/config.go

@@ -33,7 +33,16 @@ type CommonConfig struct {
 	Root           string
 	Root           string
 	TrustKeyPath   string
 	TrustKeyPath   string
 	DefaultNetwork string
 	DefaultNetwork string
-	NetworkKVStore string
+
+	// ClusterStore is the storage backend used for the cluster information. It is used by both
+	// multihost networking (to store networks and endpoints information) and by the node discovery
+	// mechanism.
+	ClusterStore string
+
+	// ClusterAdvertise is the network endpoint that the Engine advertises for the purpose of node
+	// discovery. This should be a 'host:port' combination on which that daemon instance is
+	// reachable by other hosts.
+	ClusterAdvertise string
 }
 }
 
 
 // InstallCommonFlags adds command-line options to the top-level flag parser for
 // InstallCommonFlags adds command-line options to the top-level flag parser for
@@ -57,4 +66,6 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
 	cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
 	cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
 	cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
 	cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
 	cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
 	cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
+	cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address of the daemon instance to advertise"))
+	cmd.StringVar(&config.ClusterStore, []string{"-cluster-store"}, "", usageFn("Set the cluster store"))
 }
 }

+ 0 - 1
daemon/config_experimental.go

@@ -6,5 +6,4 @@ import flag "github.com/docker/docker/pkg/mflag"
 
 
 func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
 	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
-	cmd.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", usageFn("Set KV Store configuration"))
 }
 }

+ 12 - 0
daemon/daemon.go

@@ -34,6 +34,7 @@ import (
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/broadcastwriter"
 	"github.com/docker/docker/pkg/broadcastwriter"
+	"github.com/docker/docker/pkg/discovery"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/fileutils"
 	"github.com/docker/docker/pkg/graphdb"
 	"github.com/docker/docker/pkg/graphdb"
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/ioutils"
@@ -116,6 +117,7 @@ type Daemon struct {
 	EventsService    *events.Events
 	EventsService    *events.Events
 	netController    libnetwork.NetworkController
 	netController    libnetwork.NetworkController
 	volumes          *store.VolumeStore
 	volumes          *store.VolumeStore
+	discoveryWatcher discovery.Watcher
 	root             string
 	root             string
 	shutdown         bool
 	shutdown         bool
 }
 }
@@ -751,6 +753,16 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	// Discovery is only enabled when the daemon is launched with an address to advertise.  When
+	// initialized, the daemon is registered and we can store the discovery backend as its read-only
+	// DiscoveryWatcher version.
+	if config.ClusterStore != "" && config.ClusterAdvertise != "" {
+		var err error
+		if d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise); err != nil {
+			return nil, fmt.Errorf("discovery initialization failed (%v)", err)
+		}
+	}
+
 	d.ID = trustKey.PublicKey().KeyID()
 	d.ID = trustKey.PublicKey().KeyID()
 	d.repository = daemonRepo
 	d.repository = daemonRepo
 	d.containers = &contStore{s: make(map[string]*Container)}
 	d.containers = &contStore{s: make(map[string]*Container)}

+ 2 - 2
daemon/daemon_unix.go

@@ -312,8 +312,8 @@ func networkOptions(dconfig *Config) ([]nwconfig.Option, error) {
 		options = append(options, nwconfig.OptionDefaultNetwork(dn))
 		options = append(options, nwconfig.OptionDefaultNetwork(dn))
 	}
 	}
 
 
-	if strings.TrimSpace(dconfig.NetworkKVStore) != "" {
-		kv := strings.Split(dconfig.NetworkKVStore, "://")
+	if strings.TrimSpace(dconfig.ClusterStore) != "" {
+		kv := strings.Split(dconfig.ClusterStore, "://")
 		if len(kv) < 2 {
 		if len(kv) < 2 {
 			return nil, fmt.Errorf("kv store daemon config must be of the form KV-PROVIDER://KV-URL")
 			return nil, fmt.Errorf("kv store daemon config must be of the form KV-PROVIDER://KV-URL")
 		}
 		}

+ 48 - 0
daemon/discovery.go

@@ -0,0 +1,48 @@
+package daemon
+
+import (
+	"time"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/docker/pkg/discovery"
+
+	// Register the libkv backends for discovery.
+	_ "github.com/docker/docker/pkg/discovery/kv"
+)
+
+const (
+	// defaultDiscoveryHeartbeat is the default value for discovery heartbeat interval.
+	defaultDiscoveryHeartbeat = 20 * time.Second
+
+	// defaultDiscoveryTTL is the default TTL interface for discovery.
+	defaultDiscoveryTTL = 60 * time.Second
+)
+
+// initDiscovery initialized the nodes discovery subsystem by connecting to the specified backend
+// and start a registration loop to advertise the current node under the specified address.
+func initDiscovery(backend, address string) (discovery.Backend, error) {
+	var (
+		discoveryBackend discovery.Backend
+		err              error
+	)
+	if discoveryBackend, err = discovery.New(backend, defaultDiscoveryHeartbeat, defaultDiscoveryTTL); err != nil {
+		return nil, err
+	}
+
+	// We call Register() on the discovery backend in a loop for the whole lifetime of the daemon,
+	// but we never actually Watch() for nodes appearing and disappearing for the moment.
+	go registrationLoop(discoveryBackend, address)
+	return discoveryBackend, nil
+}
+
+// registrationLoop registers the current node against the discovery backend using the specified
+// address. The function never returns, as registration against the backend comes with a TTL and
+// requires regular heartbeats.
+func registrationLoop(discoveryBackend discovery.Backend, address string) {
+	for {
+		if err := discoveryBackend.Register(address); err != nil {
+			log.Errorf("Registering as %q in discovery failed: %v", address, err)
+		}
+		time.Sleep(defaultDiscoveryHeartbeat)
+	}
+}

+ 1 - 0
daemon/info.go

@@ -92,6 +92,7 @@ func (daemon *Daemon) SystemInfo(ctx context.Context) (*types.Info, error) {
 		Labels:             daemon.config().Labels,
 		Labels:             daemon.config().Labels,
 		ExperimentalBuild:  utils.ExperimentalBuild(),
 		ExperimentalBuild:  utils.ExperimentalBuild(),
 		ServerVersion:      dockerversion.VERSION,
 		ServerVersion:      dockerversion.VERSION,
+		ClusterStore:       daemon.config().ClusterStore,
 	}
 	}
 
 
 	// TODO Windows. Refactor this more once sysinfo is refactored into
 	// TODO Windows. Refactor this more once sysinfo is refactored into

+ 1 - 0
docs/reference/api/docker_remote_api_v1.21.md

@@ -1821,6 +1821,7 @@ Display system-wide information
         "CpuCfsPeriod": true,
         "CpuCfsPeriod": true,
         "CpuCfsQuota": true,
         "CpuCfsQuota": true,
         "Debug": false,
         "Debug": false,
+        "DiscoveryBackend": "etcd://localhost:2379",
         "DockerRootDir": "/var/lib/docker",
         "DockerRootDir": "/var/lib/docker",
         "Driver": "btrfs",
         "Driver": "btrfs",
         "DriverStatus": [[""]],
         "DriverStatus": [[""]],

+ 8 - 0
docs/reference/commandline/daemon.md

@@ -22,6 +22,8 @@ weight=1
       -D, --debug=false                      Enable debug mode
       -D, --debug=false                      Enable debug mode
       --default-gateway=""                   Container default gateway IPv4 address
       --default-gateway=""                   Container default gateway IPv4 address
       --default-gateway-v6=""                Container default gateway IPv6 address
       --default-gateway-v6=""                Container default gateway IPv6 address
+      --cluster-store=""                     URL of the distributed storage backend
+      --cluster-advertise=""                 Address of the daemon instance to advertise
       --dns=[]                               DNS server to use
       --dns=[]                               DNS server to use
       --dns-opt=[]                           DNS options to use
       --dns-opt=[]                           DNS options to use
       --dns-search=[]                        DNS search domains to use
       --dns-search=[]                        DNS search domains to use
@@ -484,6 +486,12 @@ Be careful setting `nproc` with the `ulimit` flag as `nproc` is designed by Linu
 set the maximum number of processes available to a user, not to a container. For details
 set the maximum number of processes available to a user, not to a container. For details
 please check the [run](run.md) reference.
 please check the [run](run.md) reference.
 
 
+## Nodes discovery
+
+`--cluster-advertise` specifies the 'host:port' combination that this particular
+daemon instance should use when advertising itself to the cluster. The daemon
+should be reachable by remote hosts on this 'host:port' combination.
+
 ## Miscellaneous options
 ## Miscellaneous options
 
 
 IP masquerading uses address translation to allow containers without a public
 IP masquerading uses address translation to allow containers without a public

+ 2 - 2
experimental/network_overlay.md

@@ -5,10 +5,10 @@ Using the above experimental UI `docker network`, `docker service` and `--publis
 
 
 Since `network` and `service` objects are globally significant, this feature requires distributed states provided by the `libkv` project.
 Since `network` and `service` objects are globally significant, this feature requires distributed states provided by the `libkv` project.
 Using `libkv`, the user can plug any of the supported Key-Value store (such as consul, etcd or zookeeper).
 Using `libkv`, the user can plug any of the supported Key-Value store (such as consul, etcd or zookeeper).
-User can specify the Key-Value store of choice using the `--kv-store` daemon flag, which takes configuration value of format `PROVIDER:URL`, where
+User can specify the Key-Value store of choice using the `--cluster-store` daemon flag, which takes configuration value of format `PROVIDER:URL`, where
 `PROVIDER` is the name of the Key-Value store (such as consul, etcd or zookeeper) and
 `PROVIDER` is the name of the Key-Value store (such as consul, etcd or zookeeper) and
 `URL` is the url to reach the Key-Value store.
 `URL` is the url to reach the Key-Value store.
-Example : `docker daemon --kv-store=consul:localhost:8500`
+Example : `docker daemon --cluster-store=consul://localhost:8500`
 
 
 Send us feedback and comments on [#14083](https://github.com/docker/docker/issues/14083)
 Send us feedback and comments on [#14083](https://github.com/docker/docker/issues/14083)
 or on the usual Google Groups (docker-user, docker-dev) and IRC channels.
 or on the usual Google Groups (docker-user, docker-dev) and IRC channels.

+ 2 - 0
hack/.vendor-helpers.sh

@@ -108,6 +108,8 @@ clean() {
 	findArgs=(
 	findArgs=(
 		# This directory contains only .c and .h files which are necessary
 		# This directory contains only .c and .h files which are necessary
 		-path vendor/src/github.com/mattn/go-sqlite3/code
 		-path vendor/src/github.com/mattn/go-sqlite3/code
+		# This directory is needed for compiling the unit tests
+		-o -path vendor/src/github.com/stretchr/objx
 	)
 	)
 	for import in "${imports[@]}"; do
 	for import in "${imports[@]}"; do
 		[ "${#findArgs[@]}" -eq 0 ] || findArgs+=( -or )
 		[ "${#findArgs[@]}" -eq 0 ] || findArgs+=( -or )

+ 2 - 0
hack/vendor.sh

@@ -16,6 +16,8 @@ clone git github.com/kr/pty 5cf931ef8f
 clone git github.com/mattn/go-sqlite3 v1.1.0
 clone git github.com/mattn/go-sqlite3 v1.1.0
 clone git github.com/microsoft/hcsshim 7f646aa6b26bcf90caee91e93cde4a80d0d8a83e
 clone git github.com/microsoft/hcsshim 7f646aa6b26bcf90caee91e93cde4a80d0d8a83e
 clone git github.com/mistifyio/go-zfs v2.1.1
 clone git github.com/mistifyio/go-zfs v2.1.1
+clone git github.com/stretchr/objx cbeaeb16a013161a98496fad62933b1d21786672
+clone git github.com/stretchr/testify 7c2b1e5640dcf2631213ca962d892bffa1e08860
 clone git github.com/tchap/go-patricia v2.1.0
 clone git github.com/tchap/go-patricia v2.1.0
 clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
 clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
 
 

+ 19 - 0
integration-cli/docker_cli_info_test.go

@@ -1,8 +1,10 @@
 package main
 package main
 
 
 import (
 import (
+	"fmt"
 	"strings"
 	"strings"
 
 
+	"github.com/docker/docker/pkg/integration/checker"
 	"github.com/docker/docker/utils"
 	"github.com/docker/docker/utils"
 	"github.com/go-check/check"
 	"github.com/go-check/check"
 )
 )
@@ -35,3 +37,20 @@ func (s *DockerSuite) TestInfoEnsureSucceeds(c *check.C) {
 		}
 		}
 	}
 	}
 }
 }
+
+// TestInfoDiscoveryBackend verifies that a daemon run with `--cluster-advertise` and
+// `--cluster-store` properly show the backend's endpoint in info output.
+func (s *DockerSuite) TestInfoDiscoveryBackend(c *check.C) {
+	testRequires(c, SameHostDaemon)
+
+	d := NewDaemon(c)
+	discoveryBackend := "consul://consuladdr:consulport/some/path"
+	if err := d.Start(fmt.Sprintf("--cluster-store=%s", discoveryBackend), "--cluster-advertise=foo"); err != nil {
+		c.Fatal(err)
+	}
+	defer d.Stop()
+
+	out, err := d.Cmd("info")
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, fmt.Sprintf("Cluster store: %s\n", discoveryBackend))
+}

+ 41 - 0
pkg/discovery/README.md

@@ -0,0 +1,41 @@
+---
+page_title: Docker discovery
+page_description: discovery
+page_keywords: docker, clustering, discovery
+---
+
+# Discovery
+
+Docker comes with multiple Discovery backends.
+
+## Backends
+
+### Using etcd
+
+Point your Docker Engine instances to a common etcd instance. You can specify
+the address Docker uses to advertise the node using the `--discovery-address`
+flag.
+
+```bash
+$ docker daemon -H=<node_ip:2376> --discovery-address=<node_ip:2376> --discovery-backend etcd://<etcd_ip>/<path>
+```
+
+### Using consul
+
+Point your Docker Engine instances to a common Consul instance. You can specify
+the address Docker uses to advertise the node using the `--discovery-address`
+flag.
+
+```bash
+$ docker daemon -H=<node_ip:2376> --discovery-address=<node_ip:2376> --discovery-backend consul://<consul_ip>/<path>
+```
+
+### Using zookeeper
+
+Point your Docker Engine instances to a common Zookeeper instance. You can specify
+the address Docker uses to advertise the node using the `--discovery-address`
+flag.
+
+```bash
+$ docker daemon -H=<node_ip:2376> --discovery-address=<node_ip:2376> --discovery-backend zk://<zk_addr1>,<zk_addr2>>/<path>
+```

+ 53 - 0
pkg/discovery/backends.go

@@ -0,0 +1,53 @@
+package discovery
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	log "github.com/Sirupsen/logrus"
+)
+
+var (
+	// Backends is a global map of discovery backends indexed by their
+	// associated scheme.
+	backends map[string]Backend
+)
+
+func init() {
+	backends = make(map[string]Backend)
+}
+
+// Register makes a discovery backend available by the provided scheme.
+// If Register is called twice with the same scheme an error is returned.
+func Register(scheme string, d Backend) error {
+	if _, exists := backends[scheme]; exists {
+		return fmt.Errorf("scheme already registered %s", scheme)
+	}
+	log.WithField("name", scheme).Debug("Registering discovery service")
+	backends[scheme] = d
+	return nil
+}
+
+func parse(rawurl string) (string, string) {
+	parts := strings.SplitN(rawurl, "://", 2)
+
+	// nodes:port,node2:port => nodes://node1:port,node2:port
+	if len(parts) == 1 {
+		return "nodes", parts[0]
+	}
+	return parts[0], parts[1]
+}
+
+// New returns a new Discovery given a URL, heartbeat and ttl settings.
+// Returns an error if the URL scheme is not supported.
+func New(rawurl string, heartbeat time.Duration, ttl time.Duration) (Backend, error) {
+	scheme, uri := parse(rawurl)
+	if backend, exists := backends[scheme]; exists {
+		log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service")
+		err := backend.Initialize(uri, heartbeat, ttl)
+		return backend, err
+	}
+
+	return nil, ErrNotSupported
+}

+ 35 - 0
pkg/discovery/discovery.go

@@ -0,0 +1,35 @@
+package discovery
+
+import (
+	"errors"
+	"time"
+)
+
+var (
+	// ErrNotSupported is returned when a discovery service is not supported.
+	ErrNotSupported = errors.New("discovery service not supported")
+
+	// ErrNotImplemented is returned when discovery feature is not implemented
+	// by discovery backend.
+	ErrNotImplemented = errors.New("not implemented in this discovery service")
+)
+
+// Watcher provides watching over a cluster for nodes joining and leaving.
+type Watcher interface {
+	// Watch the discovery for entry changes.
+	// Returns a channel that will receive changes or an error.
+	// Providing a non-nil stopCh can be used to stop watching.
+	Watch(stopCh <-chan struct{}) (<-chan Entries, <-chan error)
+}
+
+// Backend is implemented by discovery backends which manage cluster entries.
+type Backend interface {
+	// Watcher must be provided by every backend.
+	Watcher
+
+	// Initialize the discovery with URIs, a heartbeat and a ttl.
+	Initialize(string, time.Duration, time.Duration) error
+
+	// Register to the discovery.
+	Register(string) error
+}

+ 120 - 0
pkg/discovery/discovery_test.go

@@ -0,0 +1,120 @@
+package discovery
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewEntry(t *testing.T) {
+	entry, err := NewEntry("127.0.0.1:2375")
+	assert.NoError(t, err)
+	assert.True(t, entry.Equals(&Entry{Host: "127.0.0.1", Port: "2375"}))
+	assert.Equal(t, entry.String(), "127.0.0.1:2375")
+
+	_, err = NewEntry("127.0.0.1")
+	assert.Error(t, err)
+}
+
+func TestParse(t *testing.T) {
+	scheme, uri := parse("127.0.0.1:2375")
+	assert.Equal(t, scheme, "nodes")
+	assert.Equal(t, uri, "127.0.0.1:2375")
+
+	scheme, uri = parse("localhost:2375")
+	assert.Equal(t, scheme, "nodes")
+	assert.Equal(t, uri, "localhost:2375")
+
+	scheme, uri = parse("scheme://127.0.0.1:2375")
+	assert.Equal(t, scheme, "scheme")
+	assert.Equal(t, uri, "127.0.0.1:2375")
+
+	scheme, uri = parse("scheme://localhost:2375")
+	assert.Equal(t, scheme, "scheme")
+	assert.Equal(t, uri, "localhost:2375")
+
+	scheme, uri = parse("")
+	assert.Equal(t, scheme, "nodes")
+	assert.Equal(t, uri, "")
+}
+
+func TestCreateEntries(t *testing.T) {
+	entries, err := CreateEntries(nil)
+	assert.Equal(t, entries, Entries{})
+	assert.NoError(t, err)
+
+	entries, err = CreateEntries([]string{"127.0.0.1:2375", "127.0.0.2:2375", ""})
+	assert.NoError(t, err)
+	expected := Entries{
+		&Entry{Host: "127.0.0.1", Port: "2375"},
+		&Entry{Host: "127.0.0.2", Port: "2375"},
+	}
+	assert.True(t, entries.Equals(expected))
+
+	_, err = CreateEntries([]string{"127.0.0.1", "127.0.0.2"})
+	assert.Error(t, err)
+}
+
+func TestContainsEntry(t *testing.T) {
+	entries, err := CreateEntries([]string{"127.0.0.1:2375", "127.0.0.2:2375", ""})
+	assert.NoError(t, err)
+	assert.True(t, entries.Contains(&Entry{Host: "127.0.0.1", Port: "2375"}))
+	assert.False(t, entries.Contains(&Entry{Host: "127.0.0.3", Port: "2375"}))
+}
+
+func TestEntriesEquality(t *testing.T) {
+	entries := Entries{
+		&Entry{Host: "127.0.0.1", Port: "2375"},
+		&Entry{Host: "127.0.0.2", Port: "2375"},
+	}
+
+	// Same
+	assert.True(t, entries.Equals(Entries{
+		&Entry{Host: "127.0.0.1", Port: "2375"},
+		&Entry{Host: "127.0.0.2", Port: "2375"},
+	}))
+
+	// Different size
+	assert.False(t, entries.Equals(Entries{
+		&Entry{Host: "127.0.0.1", Port: "2375"},
+		&Entry{Host: "127.0.0.2", Port: "2375"},
+		&Entry{Host: "127.0.0.3", Port: "2375"},
+	}))
+
+	// Different content
+	assert.False(t, entries.Equals(Entries{
+		&Entry{Host: "127.0.0.1", Port: "2375"},
+		&Entry{Host: "127.0.0.42", Port: "2375"},
+	}))
+}
+
+func TestEntriesDiff(t *testing.T) {
+	entry1 := &Entry{Host: "1.1.1.1", Port: "1111"}
+	entry2 := &Entry{Host: "2.2.2.2", Port: "2222"}
+	entry3 := &Entry{Host: "3.3.3.3", Port: "3333"}
+	entries := Entries{entry1, entry2}
+
+	// No diff
+	added, removed := entries.Diff(Entries{entry2, entry1})
+	assert.Empty(t, added)
+	assert.Empty(t, removed)
+
+	// Add
+	added, removed = entries.Diff(Entries{entry2, entry3, entry1})
+	assert.Len(t, added, 1)
+	assert.True(t, added.Contains(entry3))
+	assert.Empty(t, removed)
+
+	// Remove
+	added, removed = entries.Diff(Entries{entry2})
+	assert.Empty(t, added)
+	assert.Len(t, removed, 1)
+	assert.True(t, removed.Contains(entry1))
+
+	// Add and remove
+	added, removed = entries.Diff(Entries{entry1, entry3})
+	assert.Len(t, added, 1)
+	assert.True(t, added.Contains(entry3))
+	assert.Len(t, removed, 1)
+	assert.True(t, removed.Contains(entry2))
+}

+ 97 - 0
pkg/discovery/entry.go

@@ -0,0 +1,97 @@
+package discovery
+
+import (
+	"fmt"
+	"net"
+)
+
+// NewEntry creates a new entry.
+func NewEntry(url string) (*Entry, error) {
+	host, port, err := net.SplitHostPort(url)
+	if err != nil {
+		return nil, err
+	}
+	return &Entry{host, port}, nil
+}
+
+// An Entry represents a host.
+type Entry struct {
+	Host string
+	Port string
+}
+
+// Equals returns true if cmp contains the same data.
+func (e *Entry) Equals(cmp *Entry) bool {
+	return e.Host == cmp.Host && e.Port == cmp.Port
+}
+
+// String returns the string form of an entry.
+func (e *Entry) String() string {
+	return fmt.Sprintf("%s:%s", e.Host, e.Port)
+}
+
+// Entries is a list of *Entry with some helpers.
+type Entries []*Entry
+
+// Equals returns true if cmp contains the same data.
+func (e Entries) Equals(cmp Entries) bool {
+	// Check if the file has really changed.
+	if len(e) != len(cmp) {
+		return false
+	}
+	for i := range e {
+		if !e[i].Equals(cmp[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+// Contains returns true if the Entries contain a given Entry.
+func (e Entries) Contains(entry *Entry) bool {
+	for _, curr := range e {
+		if curr.Equals(entry) {
+			return true
+		}
+	}
+	return false
+}
+
+// Diff compares two entries and returns the added and removed entries.
+func (e Entries) Diff(cmp Entries) (Entries, Entries) {
+	added := Entries{}
+	for _, entry := range cmp {
+		if !e.Contains(entry) {
+			added = append(added, entry)
+		}
+	}
+
+	removed := Entries{}
+	for _, entry := range e {
+		if !cmp.Contains(entry) {
+			removed = append(removed, entry)
+		}
+	}
+
+	return added, removed
+}
+
+// CreateEntries returns an array of entries based on the given addresses.
+func CreateEntries(addrs []string) (Entries, error) {
+	entries := Entries{}
+	if addrs == nil {
+		return entries, nil
+	}
+
+	for _, addr := range addrs {
+		if len(addr) == 0 {
+			continue
+		}
+		entry, err := NewEntry(addr)
+		if err != nil {
+			return nil, err
+		}
+		entries = append(entries, entry)
+	}
+	return entries, nil
+}

+ 109 - 0
pkg/discovery/file/file.go

@@ -0,0 +1,109 @@
+package file
+
+import (
+	"fmt"
+	"io/ioutil"
+	"strings"
+	"time"
+
+	"github.com/docker/docker/pkg/discovery"
+)
+
+// Discovery is exported
+type Discovery struct {
+	heartbeat time.Duration
+	path      string
+}
+
+func init() {
+	Init()
+}
+
+// Init is exported
+func Init() {
+	discovery.Register("file", &Discovery{})
+}
+
+// Initialize is exported
+func (s *Discovery) Initialize(path string, heartbeat time.Duration, ttl time.Duration) error {
+	s.path = path
+	s.heartbeat = heartbeat
+	return nil
+}
+
+func parseFileContent(content []byte) []string {
+	var result []string
+	for _, line := range strings.Split(strings.TrimSpace(string(content)), "\n") {
+		line = strings.TrimSpace(line)
+		// Ignoring line starts with #
+		if strings.HasPrefix(line, "#") {
+			continue
+		}
+		// Inlined # comment also ignored.
+		if strings.Contains(line, "#") {
+			line = line[0:strings.Index(line, "#")]
+			// Trim additional spaces caused by above stripping.
+			line = strings.TrimSpace(line)
+		}
+		for _, ip := range discovery.Generate(line) {
+			result = append(result, ip)
+		}
+	}
+	return result
+}
+
+func (s *Discovery) fetch() (discovery.Entries, error) {
+	fileContent, err := ioutil.ReadFile(s.path)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read '%s': %v", s.path, err)
+	}
+	return discovery.CreateEntries(parseFileContent(fileContent))
+}
+
+// Watch is exported
+func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) {
+	ch := make(chan discovery.Entries)
+	errCh := make(chan error)
+	ticker := time.NewTicker(s.heartbeat)
+
+	go func() {
+		defer close(errCh)
+		defer close(ch)
+
+		// Send the initial entries if available.
+		currentEntries, err := s.fetch()
+		if err != nil {
+			errCh <- err
+		} else {
+			ch <- currentEntries
+		}
+
+		// Periodically send updates.
+		for {
+			select {
+			case <-ticker.C:
+				newEntries, err := s.fetch()
+				if err != nil {
+					errCh <- err
+					continue
+				}
+
+				// Check if the file has really changed.
+				if !newEntries.Equals(currentEntries) {
+					ch <- newEntries
+				}
+				currentEntries = newEntries
+			case <-stopCh:
+				ticker.Stop()
+				return
+			}
+		}
+	}()
+
+	return ch, errCh
+}
+
+// Register is exported
+func (s *Discovery) Register(addr string) error {
+	return discovery.ErrNotImplemented
+}

+ 106 - 0
pkg/discovery/file/file_test.go

@@ -0,0 +1,106 @@
+package file
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/docker/docker/pkg/discovery"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestInitialize(t *testing.T) {
+	d := &Discovery{}
+	d.Initialize("/path/to/file", 1000, 0)
+	assert.Equal(t, d.path, "/path/to/file")
+}
+
+func TestNew(t *testing.T) {
+	d, err := discovery.New("file:///path/to/file", 0, 0)
+	assert.NoError(t, err)
+	assert.Equal(t, d.(*Discovery).path, "/path/to/file")
+}
+
+func TestContent(t *testing.T) {
+	data := `
+1.1.1.[1:2]:1111
+2.2.2.[2:4]:2222
+`
+	ips := parseFileContent([]byte(data))
+	assert.Len(t, ips, 5)
+	assert.Equal(t, ips[0], "1.1.1.1:1111")
+	assert.Equal(t, ips[1], "1.1.1.2:1111")
+	assert.Equal(t, ips[2], "2.2.2.2:2222")
+	assert.Equal(t, ips[3], "2.2.2.3:2222")
+	assert.Equal(t, ips[4], "2.2.2.4:2222")
+}
+
+func TestRegister(t *testing.T) {
+	discovery := &Discovery{path: "/path/to/file"}
+	assert.Error(t, discovery.Register("0.0.0.0"))
+}
+
+func TestParsingContentsWithComments(t *testing.T) {
+	data := `
+### test ###
+1.1.1.1:1111 # inline comment
+# 2.2.2.2:2222
+      ### empty line with comment
+    3.3.3.3:3333
+### test ###
+`
+	ips := parseFileContent([]byte(data))
+	assert.Len(t, ips, 2)
+	assert.Equal(t, "1.1.1.1:1111", ips[0])
+	assert.Equal(t, "3.3.3.3:3333", ips[1])
+}
+
+func TestWatch(t *testing.T) {
+	data := `
+1.1.1.1:1111
+2.2.2.2:2222
+`
+	expected := discovery.Entries{
+		&discovery.Entry{Host: "1.1.1.1", Port: "1111"},
+		&discovery.Entry{Host: "2.2.2.2", Port: "2222"},
+	}
+
+	// Create a temporary file and remove it.
+	tmp, err := ioutil.TempFile(os.TempDir(), "discovery-file-test")
+	assert.NoError(t, err)
+	assert.NoError(t, tmp.Close())
+	assert.NoError(t, os.Remove(tmp.Name()))
+
+	// Set up file discovery.
+	d := &Discovery{}
+	d.Initialize(tmp.Name(), 1000, 0)
+	stopCh := make(chan struct{})
+	ch, errCh := d.Watch(stopCh)
+
+	// Make sure it fires errors since the file doesn't exist.
+	assert.Error(t, <-errCh)
+	// We have to drain the error channel otherwise Watch will get stuck.
+	go func() {
+		for range errCh {
+		}
+	}()
+
+	// Write the file and make sure we get the expected value back.
+	assert.NoError(t, ioutil.WriteFile(tmp.Name(), []byte(data), 0600))
+	assert.Equal(t, expected, <-ch)
+
+	// Add a new entry and look it up.
+	expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"})
+	f, err := os.OpenFile(tmp.Name(), os.O_APPEND|os.O_WRONLY, 0600)
+	assert.NoError(t, err)
+	assert.NotNil(t, f)
+	_, err = f.WriteString("\n3.3.3.3:3333\n")
+	assert.NoError(t, err)
+	f.Close()
+	assert.Equal(t, expected, <-ch)
+
+	// Stop and make sure it closes all channels.
+	close(stopCh)
+	assert.Nil(t, <-ch)
+	assert.Nil(t, <-errCh)
+}

+ 35 - 0
pkg/discovery/generator.go

@@ -0,0 +1,35 @@
+package discovery
+
+import (
+	"fmt"
+	"regexp"
+	"strconv"
+)
+
+// Generate takes care of IP generation
+func Generate(pattern string) []string {
+	re, _ := regexp.Compile(`\[(.+):(.+)\]`)
+	submatch := re.FindStringSubmatch(pattern)
+	if submatch == nil {
+		return []string{pattern}
+	}
+
+	from, err := strconv.Atoi(submatch[1])
+	if err != nil {
+		return []string{pattern}
+	}
+	to, err := strconv.Atoi(submatch[2])
+	if err != nil {
+		return []string{pattern}
+	}
+
+	template := re.ReplaceAllString(pattern, "%d")
+
+	var result []string
+	for val := from; val <= to; val++ {
+		entry := fmt.Sprintf(template, val)
+		result = append(result, entry)
+	}
+
+	return result
+}

+ 55 - 0
pkg/discovery/generator_test.go

@@ -0,0 +1,55 @@
+package discovery
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGeneratorNotGenerate(t *testing.T) {
+	ips := Generate("127.0.0.1")
+	assert.Equal(t, len(ips), 1)
+	assert.Equal(t, ips[0], "127.0.0.1")
+}
+
+func TestGeneratorWithPortNotGenerate(t *testing.T) {
+	ips := Generate("127.0.0.1:8080")
+	assert.Equal(t, len(ips), 1)
+	assert.Equal(t, ips[0], "127.0.0.1:8080")
+}
+
+func TestGeneratorMatchFailedNotGenerate(t *testing.T) {
+	ips := Generate("127.0.0.[1]")
+	assert.Equal(t, len(ips), 1)
+	assert.Equal(t, ips[0], "127.0.0.[1]")
+}
+
+func TestGeneratorWithPort(t *testing.T) {
+	ips := Generate("127.0.0.[1:11]:2375")
+	assert.Equal(t, len(ips), 11)
+	assert.Equal(t, ips[0], "127.0.0.1:2375")
+	assert.Equal(t, ips[1], "127.0.0.2:2375")
+	assert.Equal(t, ips[2], "127.0.0.3:2375")
+	assert.Equal(t, ips[3], "127.0.0.4:2375")
+	assert.Equal(t, ips[4], "127.0.0.5:2375")
+	assert.Equal(t, ips[5], "127.0.0.6:2375")
+	assert.Equal(t, ips[6], "127.0.0.7:2375")
+	assert.Equal(t, ips[7], "127.0.0.8:2375")
+	assert.Equal(t, ips[8], "127.0.0.9:2375")
+	assert.Equal(t, ips[9], "127.0.0.10:2375")
+	assert.Equal(t, ips[10], "127.0.0.11:2375")
+}
+
+func TestGenerateWithMalformedInputAtRangeStart(t *testing.T) {
+	malformedInput := "127.0.0.[x:11]:2375"
+	ips := Generate(malformedInput)
+	assert.Equal(t, len(ips), 1)
+	assert.Equal(t, ips[0], malformedInput)
+}
+
+func TestGenerateWithMalformedInputAtRangeEnd(t *testing.T) {
+	malformedInput := "127.0.0.[1:x]:2375"
+	ips := Generate(malformedInput)
+	assert.Equal(t, len(ips), 1)
+	assert.Equal(t, ips[0], malformedInput)
+}

+ 148 - 0
pkg/discovery/kv/kv.go

@@ -0,0 +1,148 @@
+package kv
+
+import (
+	"fmt"
+	"path"
+	"strings"
+	"time"
+
+	log "github.com/Sirupsen/logrus"
+	"github.com/docker/docker/pkg/discovery"
+	"github.com/docker/libkv"
+	"github.com/docker/libkv/store"
+	"github.com/docker/libkv/store/consul"
+	"github.com/docker/libkv/store/etcd"
+	"github.com/docker/libkv/store/zookeeper"
+)
+
+const (
+	discoveryPath = "docker/nodes"
+)
+
+// Discovery is exported
+type Discovery struct {
+	backend   store.Backend
+	store     store.Store
+	heartbeat time.Duration
+	ttl       time.Duration
+	prefix    string
+	path      string
+}
+
+func init() {
+	Init()
+}
+
+// Init is exported
+func Init() {
+	// Register to libkv
+	zookeeper.Register()
+	consul.Register()
+	etcd.Register()
+
+	// Register to internal discovery service
+	discovery.Register("zk", &Discovery{backend: store.ZK})
+	discovery.Register("consul", &Discovery{backend: store.CONSUL})
+	discovery.Register("etcd", &Discovery{backend: store.ETCD})
+}
+
+// Initialize is exported
+func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration) error {
+	var (
+		parts = strings.SplitN(uris, "/", 2)
+		addrs = strings.Split(parts[0], ",")
+		err   error
+	)
+
+	// A custom prefix to the path can be optionally used.
+	if len(parts) == 2 {
+		s.prefix = parts[1]
+	}
+
+	s.heartbeat = heartbeat
+	s.ttl = ttl
+	s.path = path.Join(s.prefix, discoveryPath)
+
+	// Creates a new store, will ignore options given
+	// if not supported by the chosen store
+	s.store, err = libkv.NewStore(s.backend, addrs, nil)
+	return err
+}
+
+// Watch the store until either there's a store error or we receive a stop request.
+// Returns false if we shouldn't attempt watching the store anymore (stop request received).
+func (s *Discovery) watchOnce(stopCh <-chan struct{}, watchCh <-chan []*store.KVPair, discoveryCh chan discovery.Entries, errCh chan error) bool {
+	for {
+		select {
+		case pairs := <-watchCh:
+			if pairs == nil {
+				return true
+			}
+
+			log.WithField("discovery", s.backend).Debugf("Watch triggered with %d nodes", len(pairs))
+
+			// Convert `KVPair` into `discovery.Entry`.
+			addrs := make([]string, len(pairs))
+			for _, pair := range pairs {
+				addrs = append(addrs, string(pair.Value))
+			}
+
+			entries, err := discovery.CreateEntries(addrs)
+			if err != nil {
+				errCh <- err
+			} else {
+				discoveryCh <- entries
+			}
+		case <-stopCh:
+			// We were requested to stop watching.
+			return false
+		}
+	}
+}
+
+// Watch is exported
+func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) {
+	ch := make(chan discovery.Entries)
+	errCh := make(chan error)
+
+	go func() {
+		defer close(ch)
+		defer close(errCh)
+
+		// Forever: Create a store watch, watch until we get an error and then try again.
+		// Will only stop if we receive a stopCh request.
+		for {
+			// Set up a watch.
+			watchCh, err := s.store.WatchTree(s.path, stopCh)
+			if err != nil {
+				errCh <- err
+			} else {
+				if !s.watchOnce(stopCh, watchCh, ch, errCh) {
+					return
+				}
+			}
+
+			// If we get here it means the store watch channel was closed. This
+			// is unexpected so let's retry later.
+			errCh <- fmt.Errorf("Unexpected watch error")
+			time.Sleep(s.heartbeat)
+		}
+	}()
+	return ch, errCh
+}
+
+// Register is exported
+func (s *Discovery) Register(addr string) error {
+	opts := &store.WriteOptions{TTL: s.ttl}
+	return s.store.Put(path.Join(s.path, addr), []byte(addr), opts)
+}
+
+// Store returns the underlying store used by KV discovery.
+func (s *Discovery) Store() store.Store {
+	return s.store
+}
+
+// Prefix returns the store prefix
+func (s *Discovery) Prefix() string {
+	return s.prefix
+}

+ 119 - 0
pkg/discovery/kv/kv_test.go

@@ -0,0 +1,119 @@
+package kv
+
+import (
+	"errors"
+	"path"
+	"testing"
+	"time"
+
+	"github.com/docker/docker/pkg/discovery"
+	"github.com/docker/libkv/store"
+	libkvmock "github.com/docker/libkv/store/mock"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+func TestInitialize(t *testing.T) {
+	storeMock, err := libkvmock.New([]string{"127.0.0.1"}, nil)
+	assert.NotNil(t, storeMock)
+	assert.NoError(t, err)
+
+	d := &Discovery{backend: store.CONSUL}
+	d.Initialize("127.0.0.1", 0, 0)
+	d.store = storeMock
+
+	s := d.store.(*libkvmock.Mock)
+	assert.Len(t, s.Endpoints, 1)
+	assert.Equal(t, s.Endpoints[0], "127.0.0.1")
+	assert.Equal(t, d.path, discoveryPath)
+
+	storeMock, err = libkvmock.New([]string{"127.0.0.1:1234"}, nil)
+	assert.NotNil(t, storeMock)
+	assert.NoError(t, err)
+
+	d = &Discovery{backend: store.CONSUL}
+	d.Initialize("127.0.0.1:1234/path", 0, 0)
+	d.store = storeMock
+
+	s = d.store.(*libkvmock.Mock)
+	assert.Len(t, s.Endpoints, 1)
+	assert.Equal(t, s.Endpoints[0], "127.0.0.1:1234")
+	assert.Equal(t, d.path, "path/"+discoveryPath)
+
+	storeMock, err = libkvmock.New([]string{"127.0.0.1:1234", "127.0.0.2:1234", "127.0.0.3:1234"}, nil)
+	assert.NotNil(t, storeMock)
+	assert.NoError(t, err)
+
+	d = &Discovery{backend: store.CONSUL}
+	d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0)
+	d.store = storeMock
+
+	s = d.store.(*libkvmock.Mock)
+	if assert.Len(t, s.Endpoints, 3) {
+		assert.Equal(t, s.Endpoints[0], "127.0.0.1:1234")
+		assert.Equal(t, s.Endpoints[1], "127.0.0.2:1234")
+		assert.Equal(t, s.Endpoints[2], "127.0.0.3:1234")
+	}
+	assert.Equal(t, d.path, "path/"+discoveryPath)
+}
+
+func TestWatch(t *testing.T) {
+	storeMock, err := libkvmock.New([]string{"127.0.0.1:1234"}, nil)
+	assert.NotNil(t, storeMock)
+	assert.NoError(t, err)
+
+	d := &Discovery{backend: store.CONSUL}
+	d.Initialize("127.0.0.1:1234/path", 0, 0)
+	d.store = storeMock
+
+	s := d.store.(*libkvmock.Mock)
+	mockCh := make(chan []*store.KVPair)
+
+	// The first watch will fail.
+	s.On("WatchTree", "path/"+discoveryPath, mock.Anything).Return(mockCh, errors.New("test error")).Once()
+	// The second one will succeed.
+	s.On("WatchTree", "path/"+discoveryPath, mock.Anything).Return(mockCh, nil).Once()
+	expected := discovery.Entries{
+		&discovery.Entry{Host: "1.1.1.1", Port: "1111"},
+		&discovery.Entry{Host: "2.2.2.2", Port: "2222"},
+	}
+	kvs := []*store.KVPair{
+		{Key: path.Join("path", discoveryPath, "1.1.1.1"), Value: []byte("1.1.1.1:1111")},
+		{Key: path.Join("path", discoveryPath, "2.2.2.2"), Value: []byte("2.2.2.2:2222")},
+	}
+
+	stopCh := make(chan struct{})
+	ch, errCh := d.Watch(stopCh)
+
+	// It should fire an error since the first WatchTree call failed.
+	assert.EqualError(t, <-errCh, "test error")
+	// We have to drain the error channel otherwise Watch will get stuck.
+	go func() {
+		for range errCh {
+		}
+	}()
+
+	// Push the entries into the store channel and make sure discovery emits.
+	mockCh <- kvs
+	assert.Equal(t, <-ch, expected)
+
+	// Add a new entry.
+	expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"})
+	kvs = append(kvs, &store.KVPair{Key: path.Join("path", discoveryPath, "3.3.3.3"), Value: []byte("3.3.3.3:3333")})
+	mockCh <- kvs
+	assert.Equal(t, <-ch, expected)
+
+	// Make sure that if an error occurs it retries.
+	// This third call to WatchTree will be checked later by AssertExpectations.
+	s.On("WatchTree", "path/"+discoveryPath, mock.Anything).Return(mockCh, nil)
+	close(mockCh)
+	// Give it enough time to call WatchTree.
+	time.Sleep(3)
+
+	// Stop and make sure it closes all channels.
+	close(stopCh)
+	assert.Nil(t, <-ch)
+	assert.Nil(t, <-errCh)
+
+	s.AssertExpectations(t)
+}

+ 54 - 0
pkg/discovery/nodes/nodes.go

@@ -0,0 +1,54 @@
+package nodes
+
+import (
+	"fmt"
+	"strings"
+	"time"
+
+	"github.com/docker/docker/pkg/discovery"
+)
+
+// Discovery is exported
+type Discovery struct {
+	entries discovery.Entries
+}
+
+func init() {
+	Init()
+}
+
+// Init is exported
+func Init() {
+	discovery.Register("nodes", &Discovery{})
+}
+
+// Initialize is exported
+func (s *Discovery) Initialize(uris string, _ time.Duration, _ time.Duration) error {
+	for _, input := range strings.Split(uris, ",") {
+		for _, ip := range discovery.Generate(input) {
+			entry, err := discovery.NewEntry(ip)
+			if err != nil {
+				return fmt.Errorf("%s, please check you are using the correct discovery (missing token:// ?)", err.Error())
+			}
+			s.entries = append(s.entries, entry)
+		}
+	}
+
+	return nil
+}
+
+// Watch is exported
+func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) {
+	ch := make(chan discovery.Entries)
+	go func() {
+		defer close(ch)
+		ch <- s.entries
+		<-stopCh
+	}()
+	return ch, nil
+}
+
+// Register is exported
+func (s *Discovery) Register(addr string) error {
+	return discovery.ErrNotImplemented
+}

+ 43 - 0
pkg/discovery/nodes/nodes_test.go

@@ -0,0 +1,43 @@
+package nodes
+
+import (
+	"testing"
+
+	"github.com/docker/docker/pkg/discovery"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestInitialize(t *testing.T) {
+	d := &Discovery{}
+	d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0)
+	assert.Equal(t, len(d.entries), 2)
+	assert.Equal(t, d.entries[0].String(), "1.1.1.1:1111")
+	assert.Equal(t, d.entries[1].String(), "2.2.2.2:2222")
+}
+
+func TestInitializeWithPattern(t *testing.T) {
+	d := &Discovery{}
+	d.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0, 0)
+	assert.Equal(t, len(d.entries), 5)
+	assert.Equal(t, d.entries[0].String(), "1.1.1.1:1111")
+	assert.Equal(t, d.entries[1].String(), "1.1.1.2:1111")
+	assert.Equal(t, d.entries[2].String(), "2.2.2.2:2222")
+	assert.Equal(t, d.entries[3].String(), "2.2.2.3:2222")
+	assert.Equal(t, d.entries[4].String(), "2.2.2.4:2222")
+}
+
+func TestWatch(t *testing.T) {
+	d := &Discovery{}
+	d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0)
+	expected := discovery.Entries{
+		&discovery.Entry{Host: "1.1.1.1", Port: "1111"},
+		&discovery.Entry{Host: "2.2.2.2", Port: "2222"},
+	}
+	ch, _ := d.Watch(nil)
+	assert.True(t, expected.Equals(<-ch))
+}
+
+func TestRegister(t *testing.T) {
+	d := &Discovery{}
+	assert.Error(t, d.Register("0.0.0.0"))
+}

+ 113 - 0
vendor/src/github.com/docker/libkv/store/mock/mock.go

@@ -0,0 +1,113 @@
+package mock
+
+import (
+	"github.com/docker/libkv/store"
+	"github.com/stretchr/testify/mock"
+)
+
+// Mock store. Mocks all Store functions using testify.Mock
+type Mock struct {
+	mock.Mock
+
+	// Endpoints passed to InitializeMock
+	Endpoints []string
+
+	// Options passed to InitializeMock
+	Options *store.Config
+}
+
+// New creates a Mock store
+func New(endpoints []string, options *store.Config) (store.Store, error) {
+	s := &Mock{}
+	s.Endpoints = endpoints
+	s.Options = options
+	return s, nil
+}
+
+// Put mock
+func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error {
+	args := s.Mock.Called(key, value, opts)
+	return args.Error(0)
+}
+
+// Get mock
+func (s *Mock) Get(key string) (*store.KVPair, error) {
+	args := s.Mock.Called(key)
+	return args.Get(0).(*store.KVPair), args.Error(1)
+}
+
+// Delete mock
+func (s *Mock) Delete(key string) error {
+	args := s.Mock.Called(key)
+	return args.Error(0)
+}
+
+// Exists mock
+func (s *Mock) Exists(key string) (bool, error) {
+	args := s.Mock.Called(key)
+	return args.Bool(0), args.Error(1)
+}
+
+// Watch mock
+func (s *Mock) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
+	args := s.Mock.Called(key, stopCh)
+	return args.Get(0).(<-chan *store.KVPair), args.Error(1)
+}
+
+// WatchTree mock
+func (s *Mock) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
+	args := s.Mock.Called(prefix, stopCh)
+	return args.Get(0).(chan []*store.KVPair), args.Error(1)
+}
+
+// NewLock mock
+func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
+	args := s.Mock.Called(key, options)
+	return args.Get(0).(store.Locker), args.Error(1)
+}
+
+// List mock
+func (s *Mock) List(prefix string) ([]*store.KVPair, error) {
+	args := s.Mock.Called(prefix)
+	return args.Get(0).([]*store.KVPair), args.Error(1)
+}
+
+// DeleteTree mock
+func (s *Mock) DeleteTree(prefix string) error {
+	args := s.Mock.Called(prefix)
+	return args.Error(0)
+}
+
+// AtomicPut mock
+func (s *Mock) AtomicPut(key string, value []byte, previous *store.KVPair, opts *store.WriteOptions) (bool, *store.KVPair, error) {
+	args := s.Mock.Called(key, value, previous, opts)
+	return args.Bool(0), args.Get(1).(*store.KVPair), args.Error(2)
+}
+
+// AtomicDelete mock
+func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
+	args := s.Mock.Called(key, previous)
+	return args.Bool(0), args.Error(1)
+}
+
+// Lock mock implementation of Locker
+type Lock struct {
+	mock.Mock
+}
+
+// Lock mock
+func (l *Lock) Lock() (<-chan struct{}, error) {
+	args := l.Mock.Called()
+	return args.Get(0).(<-chan struct{}), args.Error(1)
+}
+
+// Unlock mock
+func (l *Lock) Unlock() error {
+	args := l.Mock.Called()
+	return args.Error(0)
+}
+
+// Close mock
+func (s *Mock) Close() {
+	return
+}

+ 22 - 0
vendor/src/github.com/stretchr/objx/.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

+ 23 - 0
vendor/src/github.com/stretchr/objx/LICENSE.md

@@ -0,0 +1,23 @@
+objx - by Mat Ryer and Tyler Bunnell
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Stretchr, 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.

+ 3 - 0
vendor/src/github.com/stretchr/objx/README.md

@@ -0,0 +1,3 @@
+# objx
+
+  * Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx)

+ 179 - 0
vendor/src/github.com/stretchr/objx/accessors.go

@@ -0,0 +1,179 @@
+package objx
+
+import (
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// arrayAccesRegexString is the regex used to extract the array number
+// from the access path
+const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
+
+// arrayAccesRegex is the compiled arrayAccesRegexString
+var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
+
+// Get gets the value using the specified selector and
+// returns it inside a new Obj object.
+//
+// If it cannot find the value, Get will return a nil
+// value inside an instance of Obj.
+//
+// Get can only operate directly on map[string]interface{} and []interface.
+//
+// Example
+//
+// To access the title of the third chapter of the second book, do:
+//
+//    o.Get("books[1].chapters[2].title")
+func (m Map) Get(selector string) *Value {
+	rawObj := access(m, selector, nil, false, false)
+	return &Value{data: rawObj}
+}
+
+// Set sets the value using the specified selector and
+// returns the object on which Set was called.
+//
+// Set can only operate directly on map[string]interface{} and []interface
+//
+// Example
+//
+// To set the title of the third chapter of the second book, do:
+//
+//    o.Set("books[1].chapters[2].title","Time to Go")
+func (m Map) Set(selector string, value interface{}) Map {
+	access(m, selector, value, true, false)
+	return m
+}
+
+// access accesses the object using the selector and performs the
+// appropriate action.
+func access(current, selector, value interface{}, isSet, panics bool) interface{} {
+
+	switch selector.(type) {
+	case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+
+		if array, ok := current.([]interface{}); ok {
+			index := intFromInterface(selector)
+
+			if index >= len(array) {
+				if panics {
+					panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
+				}
+				return nil
+			}
+
+			return array[index]
+		}
+
+		return nil
+
+	case string:
+
+		selStr := selector.(string)
+		selSegs := strings.SplitN(selStr, PathSeparator, 2)
+		thisSel := selSegs[0]
+		index := -1
+		var err error
+
+		// https://github.com/stretchr/objx/issues/12
+		if strings.Contains(thisSel, "[") {
+
+			arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
+
+			if len(arrayMatches) > 0 {
+
+				// Get the key into the map
+				thisSel = arrayMatches[1]
+
+				// Get the index into the array at the key
+				index, err = strconv.Atoi(arrayMatches[2])
+
+				if err != nil {
+					// This should never happen. If it does, something has gone
+					// seriously wrong. Panic.
+					panic("objx: Array index is not an integer.  Must use array[int].")
+				}
+
+			}
+		}
+
+		if curMap, ok := current.(Map); ok {
+			current = map[string]interface{}(curMap)
+		}
+
+		// get the object in question
+		switch current.(type) {
+		case map[string]interface{}:
+			curMSI := current.(map[string]interface{})
+			if len(selSegs) <= 1 && isSet {
+				curMSI[thisSel] = value
+				return nil
+			} else {
+				current = curMSI[thisSel]
+			}
+		default:
+			current = nil
+		}
+
+		if current == nil && panics {
+			panic(fmt.Sprintf("objx: '%v' invalid on object.", selector))
+		}
+
+		// do we need to access the item of an array?
+		if index > -1 {
+			if array, ok := current.([]interface{}); ok {
+				if index < len(array) {
+					current = array[index]
+				} else {
+					if panics {
+						panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
+					}
+					current = nil
+				}
+			}
+		}
+
+		if len(selSegs) > 1 {
+			current = access(current, selSegs[1], value, isSet, panics)
+		}
+
+	}
+
+	return current
+
+}
+
+// intFromInterface converts an interface object to the largest
+// representation of an unsigned integer using a type switch and
+// assertions
+func intFromInterface(selector interface{}) int {
+	var value int
+	switch selector.(type) {
+	case int:
+		value = selector.(int)
+	case int8:
+		value = int(selector.(int8))
+	case int16:
+		value = int(selector.(int16))
+	case int32:
+		value = int(selector.(int32))
+	case int64:
+		value = int(selector.(int64))
+	case uint:
+		value = int(selector.(uint))
+	case uint8:
+		value = int(selector.(uint8))
+	case uint16:
+		value = int(selector.(uint16))
+	case uint32:
+		value = int(selector.(uint32))
+	case uint64:
+		value = int(selector.(uint64))
+	default:
+		panic("objx: array access argument is not an integer type (this should never happen)")
+	}
+
+	return value
+}

+ 13 - 0
vendor/src/github.com/stretchr/objx/constants.go

@@ -0,0 +1,13 @@
+package objx
+
+const (
+	// PathSeparator is the character used to separate the elements
+	// of the keypath.
+	//
+	// For example, `location.address.city`
+	PathSeparator string = "."
+
+	// SignatureSeparator is the character that is used to
+	// separate the Base64 string from the security signature.
+	SignatureSeparator = "_"
+)

+ 117 - 0
vendor/src/github.com/stretchr/objx/conversions.go

@@ -0,0 +1,117 @@
+package objx
+
+import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/url"
+)
+
+// JSON converts the contained object to a JSON string
+// representation
+func (m Map) JSON() (string, error) {
+
+	result, err := json.Marshal(m)
+
+	if err != nil {
+		err = errors.New("objx: JSON encode failed with: " + err.Error())
+	}
+
+	return string(result), err
+
+}
+
+// MustJSON converts the contained object to a JSON string
+// representation and panics if there is an error
+func (m Map) MustJSON() string {
+	result, err := m.JSON()
+	if err != nil {
+		panic(err.Error())
+	}
+	return result
+}
+
+// Base64 converts the contained object to a Base64 string
+// representation of the JSON string representation
+func (m Map) Base64() (string, error) {
+
+	var buf bytes.Buffer
+
+	jsonData, err := m.JSON()
+	if err != nil {
+		return "", err
+	}
+
+	encoder := base64.NewEncoder(base64.StdEncoding, &buf)
+	encoder.Write([]byte(jsonData))
+	encoder.Close()
+
+	return buf.String(), nil
+
+}
+
+// MustBase64 converts the contained object to a Base64 string
+// representation of the JSON string representation and panics
+// if there is an error
+func (m Map) MustBase64() string {
+	result, err := m.Base64()
+	if err != nil {
+		panic(err.Error())
+	}
+	return result
+}
+
+// SignedBase64 converts the contained object to a Base64 string
+// representation of the JSON string representation and signs it
+// using the provided key.
+func (m Map) SignedBase64(key string) (string, error) {
+
+	base64, err := m.Base64()
+	if err != nil {
+		return "", err
+	}
+
+	sig := HashWithKey(base64, key)
+
+	return base64 + SignatureSeparator + sig, nil
+
+}
+
+// MustSignedBase64 converts the contained object to a Base64 string
+// representation of the JSON string representation and signs it
+// using the provided key and panics if there is an error
+func (m Map) MustSignedBase64(key string) string {
+	result, err := m.SignedBase64(key)
+	if err != nil {
+		panic(err.Error())
+	}
+	return result
+}
+
+/*
+	URL Query
+	------------------------------------------------
+*/
+
+// URLValues creates a url.Values object from an Obj. This
+// function requires that the wrapped object be a map[string]interface{}
+func (m Map) URLValues() url.Values {
+
+	vals := make(url.Values)
+
+	for k, v := range m {
+		//TODO: can this be done without sprintf?
+		vals.Set(k, fmt.Sprintf("%v", v))
+	}
+
+	return vals
+}
+
+// URLQuery gets an encoded URL query representing the given
+// Obj. This function requires that the wrapped object be a
+// map[string]interface{}
+func (m Map) URLQuery() (string, error) {
+	return m.URLValues().Encode(), nil
+}

+ 72 - 0
vendor/src/github.com/stretchr/objx/doc.go

@@ -0,0 +1,72 @@
+// objx - Go package for dealing with maps, slices, JSON and other data.
+//
+// Overview
+//
+// Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
+// a powerful `Get` method (among others) that allows you to easily and quickly get
+// access to data within the map, without having to worry too much about type assertions,
+// missing data, default values etc.
+//
+// Pattern
+//
+// Objx uses a preditable pattern to make access data from within `map[string]interface{}'s
+// easy.
+//
+// Call one of the `objx.` functions to create your `objx.Map` to get going:
+//
+//     m, err := objx.FromJSON(json)
+//
+// NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
+// the rest will be optimistic and try to figure things out without panicking.
+//
+// Use `Get` to access the value you're interested in.  You can use dot and array
+// notation too:
+//
+//     m.Get("places[0].latlng")
+//
+// Once you have saught the `Value` you're interested in, you can use the `Is*` methods
+// to determine its type.
+//
+//     if m.Get("code").IsStr() { /* ... */ }
+//
+// Or you can just assume the type, and use one of the strong type methods to
+// extract the real value:
+//
+//     m.Get("code").Int()
+//
+// If there's no value there (or if it's the wrong type) then a default value
+// will be returned, or you can be explicit about the default value.
+//
+//     Get("code").Int(-1)
+//
+// If you're dealing with a slice of data as a value, Objx provides many useful
+// methods for iterating, manipulating and selecting that data.  You can find out more
+// by exploring the index below.
+//
+// Reading data
+//
+// A simple example of how to use Objx:
+//
+//     // use MustFromJSON to make an objx.Map from some JSON
+//     m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
+//
+//     // get the details
+//     name := m.Get("name").Str()
+//     age := m.Get("age").Int()
+//
+//     // get their nickname (or use their name if they
+//     // don't have one)
+//     nickname := m.Get("nickname").Str(name)
+//
+// Ranging
+//
+// Since `objx.Map` is a `map[string]interface{}` you can treat it as such.  For
+// example, to `range` the data, do what you would expect:
+//
+//     m := objx.MustFromJSON(json)
+//     for key, value := range m {
+//
+//       /* ... do your magic ... */
+//
+//     }
+package objx

+ 222 - 0
vendor/src/github.com/stretchr/objx/map.go

@@ -0,0 +1,222 @@
+package objx
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"io/ioutil"
+	"net/url"
+	"strings"
+)
+
+// MSIConvertable is an interface that defines methods for converting your
+// custom types to a map[string]interface{} representation.
+type MSIConvertable interface {
+	// MSI gets a map[string]interface{} (msi) representing the
+	// object.
+	MSI() map[string]interface{}
+}
+
+// Map provides extended functionality for working with
+// untyped data, in particular map[string]interface (msi).
+type Map map[string]interface{}
+
+// Value returns the internal value instance
+func (m Map) Value() *Value {
+	return &Value{data: m}
+}
+
+// Nil represents a nil Map.
+var Nil Map = New(nil)
+
+// New creates a new Map containing the map[string]interface{} in the data argument.
+// If the data argument is not a map[string]interface, New attempts to call the
+// MSI() method on the MSIConvertable interface to create one.
+func New(data interface{}) Map {
+	if _, ok := data.(map[string]interface{}); !ok {
+		if converter, ok := data.(MSIConvertable); ok {
+			data = converter.MSI()
+		} else {
+			return nil
+		}
+	}
+	return Map(data.(map[string]interface{}))
+}
+
+// MSI creates a map[string]interface{} and puts it inside a new Map.
+//
+// The arguments follow a key, value pattern.
+//
+// Panics
+//
+// Panics if any key arugment is non-string or if there are an odd number of arguments.
+//
+// Example
+//
+// To easily create Maps:
+//
+//     m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
+//
+//     // creates an Map equivalent to
+//     m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}})
+func MSI(keyAndValuePairs ...interface{}) Map {
+
+	newMap := make(map[string]interface{})
+	keyAndValuePairsLen := len(keyAndValuePairs)
+
+	if keyAndValuePairsLen%2 != 0 {
+		panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.")
+	}
+
+	for i := 0; i < keyAndValuePairsLen; i = i + 2 {
+
+		key := keyAndValuePairs[i]
+		value := keyAndValuePairs[i+1]
+
+		// make sure the key is a string
+		keyString, keyStringOK := key.(string)
+		if !keyStringOK {
+			panic("objx: MSI must follow 'string, interface{}' pattern.  " + keyString + " is not a valid key.")
+		}
+
+		newMap[keyString] = value
+
+	}
+
+	return New(newMap)
+}
+
+// ****** Conversion Constructors
+
+// MustFromJSON creates a new Map containing the data specified in the
+// jsonString.
+//
+// Panics if the JSON is invalid.
+func MustFromJSON(jsonString string) Map {
+	o, err := FromJSON(jsonString)
+
+	if err != nil {
+		panic("objx: MustFromJSON failed with error: " + err.Error())
+	}
+
+	return o
+}
+
+// FromJSON creates a new Map containing the data specified in the
+// jsonString.
+//
+// Returns an error if the JSON is invalid.
+func FromJSON(jsonString string) (Map, error) {
+
+	var data interface{}
+	err := json.Unmarshal([]byte(jsonString), &data)
+
+	if err != nil {
+		return Nil, err
+	}
+
+	return New(data), nil
+
+}
+
+// FromBase64 creates a new Obj containing the data specified
+// in the Base64 string.
+//
+// The string is an encoded JSON string returned by Base64
+func FromBase64(base64String string) (Map, error) {
+
+	decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
+
+	decoded, err := ioutil.ReadAll(decoder)
+	if err != nil {
+		return nil, err
+	}
+
+	return FromJSON(string(decoded))
+}
+
+// MustFromBase64 creates a new Obj containing the data specified
+// in the Base64 string and panics if there is an error.
+//
+// The string is an encoded JSON string returned by Base64
+func MustFromBase64(base64String string) Map {
+
+	result, err := FromBase64(base64String)
+
+	if err != nil {
+		panic("objx: MustFromBase64 failed with error: " + err.Error())
+	}
+
+	return result
+}
+
+// FromSignedBase64 creates a new Obj containing the data specified
+// in the Base64 string.
+//
+// The string is an encoded JSON string returned by SignedBase64
+func FromSignedBase64(base64String, key string) (Map, error) {
+	parts := strings.Split(base64String, SignatureSeparator)
+	if len(parts) != 2 {
+		return nil, errors.New("objx: Signed base64 string is malformed.")
+	}
+
+	sig := HashWithKey(parts[0], key)
+	if parts[1] != sig {
+		return nil, errors.New("objx: Signature for base64 data does not match.")
+	}
+
+	return FromBase64(parts[0])
+}
+
+// MustFromSignedBase64 creates a new Obj containing the data specified
+// in the Base64 string and panics if there is an error.
+//
+// The string is an encoded JSON string returned by Base64
+func MustFromSignedBase64(base64String, key string) Map {
+
+	result, err := FromSignedBase64(base64String, key)
+
+	if err != nil {
+		panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
+	}
+
+	return result
+}
+
+// FromURLQuery generates a new Obj by parsing the specified
+// query.
+//
+// For queries with multiple values, the first value is selected.
+func FromURLQuery(query string) (Map, error) {
+
+	vals, err := url.ParseQuery(query)
+
+	if err != nil {
+		return nil, err
+	}
+
+	m := make(map[string]interface{})
+	for k, vals := range vals {
+		m[k] = vals[0]
+	}
+
+	return New(m), nil
+}
+
+// MustFromURLQuery generates a new Obj by parsing the specified
+// query.
+//
+// For queries with multiple values, the first value is selected.
+//
+// Panics if it encounters an error
+func MustFromURLQuery(query string) Map {
+
+	o, err := FromURLQuery(query)
+
+	if err != nil {
+		panic("objx: MustFromURLQuery failed with error: " + err.Error())
+	}
+
+	return o
+
+}

+ 81 - 0
vendor/src/github.com/stretchr/objx/mutations.go

@@ -0,0 +1,81 @@
+package objx
+
+// Exclude returns a new Map with the keys in the specified []string
+// excluded.
+func (d Map) Exclude(exclude []string) Map {
+
+	excluded := make(Map)
+	for k, v := range d {
+		var shouldInclude bool = true
+		for _, toExclude := range exclude {
+			if k == toExclude {
+				shouldInclude = false
+				break
+			}
+		}
+		if shouldInclude {
+			excluded[k] = v
+		}
+	}
+
+	return excluded
+}
+
+// Copy creates a shallow copy of the Obj.
+func (m Map) Copy() Map {
+	copied := make(map[string]interface{})
+	for k, v := range m {
+		copied[k] = v
+	}
+	return New(copied)
+}
+
+// Merge blends the specified map with a copy of this map and returns the result.
+//
+// Keys that appear in both will be selected from the specified map.
+// This method requires that the wrapped object be a map[string]interface{}
+func (m Map) Merge(merge Map) Map {
+	return m.Copy().MergeHere(merge)
+}
+
+// Merge blends the specified map with this map and returns the current map.
+//
+// Keys that appear in both will be selected from the specified map.  The original map
+// will be modified. This method requires that
+// the wrapped object be a map[string]interface{}
+func (m Map) MergeHere(merge Map) Map {
+
+	for k, v := range merge {
+		m[k] = v
+	}
+
+	return m
+
+}
+
+// Transform builds a new Obj giving the transformer a chance
+// to change the keys and values as it goes. This method requires that
+// the wrapped object be a map[string]interface{}
+func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
+	newMap := make(map[string]interface{})
+	for k, v := range m {
+		modifiedKey, modifiedVal := transformer(k, v)
+		newMap[modifiedKey] = modifiedVal
+	}
+	return New(newMap)
+}
+
+// TransformKeys builds a new map using the specified key mapping.
+//
+// Unspecified keys will be unaltered.
+// This method requires that the wrapped object be a map[string]interface{}
+func (m Map) TransformKeys(mapping map[string]string) Map {
+	return m.Transform(func(key string, value interface{}) (string, interface{}) {
+
+		if newKey, ok := mapping[key]; ok {
+			return newKey, value
+		}
+
+		return key, value
+	})
+}

+ 14 - 0
vendor/src/github.com/stretchr/objx/security.go

@@ -0,0 +1,14 @@
+package objx
+
+import (
+	"crypto/sha1"
+	"encoding/hex"
+)
+
+// HashWithKey hashes the specified string using the security
+// key.
+func HashWithKey(data, key string) string {
+	hash := sha1.New()
+	hash.Write([]byte(data + ":" + key))
+	return hex.EncodeToString(hash.Sum(nil))
+}

+ 17 - 0
vendor/src/github.com/stretchr/objx/tests.go

@@ -0,0 +1,17 @@
+package objx
+
+// Has gets whether there is something at the specified selector
+// or not.
+//
+// If m is nil, Has will always return false.
+func (m Map) Has(selector string) bool {
+	if m == nil {
+		return false
+	}
+	return !m.Get(selector).IsNil()
+}
+
+// IsNil gets whether the data is nil or not.
+func (v *Value) IsNil() bool {
+	return v == nil || v.data == nil
+}

+ 2881 - 0
vendor/src/github.com/stretchr/objx/type_specific_codegen.go

@@ -0,0 +1,2881 @@
+package objx
+
+/*
+	Inter (interface{} and []interface{})
+	--------------------------------------------------
+*/
+
+// Inter gets the value as a interface{}, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Inter(optionalDefault ...interface{}) interface{} {
+	if s, ok := v.data.(interface{}); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustInter gets the value as a interface{}.
+//
+// Panics if the object is not a interface{}.
+func (v *Value) MustInter() interface{} {
+	return v.data.(interface{})
+}
+
+// InterSlice gets the value as a []interface{}, returns the optionalDefault
+// value or nil if the value is not a []interface{}.
+func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} {
+	if s, ok := v.data.([]interface{}); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustInterSlice gets the value as a []interface{}.
+//
+// Panics if the object is not a []interface{}.
+func (v *Value) MustInterSlice() []interface{} {
+	return v.data.([]interface{})
+}
+
+// IsInter gets whether the object contained is a interface{} or not.
+func (v *Value) IsInter() bool {
+	_, ok := v.data.(interface{})
+	return ok
+}
+
+// IsInterSlice gets whether the object contained is a []interface{} or not.
+func (v *Value) IsInterSlice() bool {
+	_, ok := v.data.([]interface{})
+	return ok
+}
+
+// EachInter calls the specified callback for each object
+// in the []interface{}.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInter(callback func(int, interface{}) bool) *Value {
+
+	for index, val := range v.MustInterSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereInter uses the specified decider function to select items
+// from the []interface{}.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value {
+
+	var selected []interface{}
+
+	v.EachInter(func(index int, val interface{}) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupInter uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]interface{}.
+func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value {
+
+	groups := make(map[string][]interface{})
+
+	v.EachInter(func(index int, val interface{}) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]interface{}, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceInter uses the specified function to replace each interface{}s
+// by iterating each item.  The data in the returned result will be a
+// []interface{} containing the replaced items.
+func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value {
+
+	arr := v.MustInterSlice()
+	replaced := make([]interface{}, len(arr))
+
+	v.EachInter(func(index int, val interface{}) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectInter uses the specified collector function to collect a value
+// for each of the interface{}s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value {
+
+	arr := v.MustInterSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachInter(func(index int, val interface{}) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	MSI (map[string]interface{} and []map[string]interface{})
+	--------------------------------------------------
+*/
+
+// MSI gets the value as a map[string]interface{}, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} {
+	if s, ok := v.data.(map[string]interface{}); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustMSI gets the value as a map[string]interface{}.
+//
+// Panics if the object is not a map[string]interface{}.
+func (v *Value) MustMSI() map[string]interface{} {
+	return v.data.(map[string]interface{})
+}
+
+// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault
+// value or nil if the value is not a []map[string]interface{}.
+func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} {
+	if s, ok := v.data.([]map[string]interface{}); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustMSISlice gets the value as a []map[string]interface{}.
+//
+// Panics if the object is not a []map[string]interface{}.
+func (v *Value) MustMSISlice() []map[string]interface{} {
+	return v.data.([]map[string]interface{})
+}
+
+// IsMSI gets whether the object contained is a map[string]interface{} or not.
+func (v *Value) IsMSI() bool {
+	_, ok := v.data.(map[string]interface{})
+	return ok
+}
+
+// IsMSISlice gets whether the object contained is a []map[string]interface{} or not.
+func (v *Value) IsMSISlice() bool {
+	_, ok := v.data.([]map[string]interface{})
+	return ok
+}
+
+// EachMSI calls the specified callback for each object
+// in the []map[string]interface{}.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value {
+
+	for index, val := range v.MustMSISlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereMSI uses the specified decider function to select items
+// from the []map[string]interface{}.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value {
+
+	var selected []map[string]interface{}
+
+	v.EachMSI(func(index int, val map[string]interface{}) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupMSI uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]map[string]interface{}.
+func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value {
+
+	groups := make(map[string][]map[string]interface{})
+
+	v.EachMSI(func(index int, val map[string]interface{}) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]map[string]interface{}, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceMSI uses the specified function to replace each map[string]interface{}s
+// by iterating each item.  The data in the returned result will be a
+// []map[string]interface{} containing the replaced items.
+func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value {
+
+	arr := v.MustMSISlice()
+	replaced := make([]map[string]interface{}, len(arr))
+
+	v.EachMSI(func(index int, val map[string]interface{}) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectMSI uses the specified collector function to collect a value
+// for each of the map[string]interface{}s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value {
+
+	arr := v.MustMSISlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachMSI(func(index int, val map[string]interface{}) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	ObjxMap ((Map) and [](Map))
+	--------------------------------------------------
+*/
+
+// ObjxMap gets the value as a (Map), returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) ObjxMap(optionalDefault ...(Map)) Map {
+	if s, ok := v.data.((Map)); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return New(nil)
+}
+
+// MustObjxMap gets the value as a (Map).
+//
+// Panics if the object is not a (Map).
+func (v *Value) MustObjxMap() Map {
+	return v.data.((Map))
+}
+
+// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault
+// value or nil if the value is not a [](Map).
+func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) {
+	if s, ok := v.data.([](Map)); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustObjxMapSlice gets the value as a [](Map).
+//
+// Panics if the object is not a [](Map).
+func (v *Value) MustObjxMapSlice() [](Map) {
+	return v.data.([](Map))
+}
+
+// IsObjxMap gets whether the object contained is a (Map) or not.
+func (v *Value) IsObjxMap() bool {
+	_, ok := v.data.((Map))
+	return ok
+}
+
+// IsObjxMapSlice gets whether the object contained is a [](Map) or not.
+func (v *Value) IsObjxMapSlice() bool {
+	_, ok := v.data.([](Map))
+	return ok
+}
+
+// EachObjxMap calls the specified callback for each object
+// in the [](Map).
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value {
+
+	for index, val := range v.MustObjxMapSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereObjxMap uses the specified decider function to select items
+// from the [](Map).  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value {
+
+	var selected [](Map)
+
+	v.EachObjxMap(func(index int, val Map) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupObjxMap uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][](Map).
+func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value {
+
+	groups := make(map[string][](Map))
+
+	v.EachObjxMap(func(index int, val Map) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([](Map), 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceObjxMap uses the specified function to replace each (Map)s
+// by iterating each item.  The data in the returned result will be a
+// [](Map) containing the replaced items.
+func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value {
+
+	arr := v.MustObjxMapSlice()
+	replaced := make([](Map), len(arr))
+
+	v.EachObjxMap(func(index int, val Map) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectObjxMap uses the specified collector function to collect a value
+// for each of the (Map)s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value {
+
+	arr := v.MustObjxMapSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachObjxMap(func(index int, val Map) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Bool (bool and []bool)
+	--------------------------------------------------
+*/
+
+// Bool gets the value as a bool, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Bool(optionalDefault ...bool) bool {
+	if s, ok := v.data.(bool); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return false
+}
+
+// MustBool gets the value as a bool.
+//
+// Panics if the object is not a bool.
+func (v *Value) MustBool() bool {
+	return v.data.(bool)
+}
+
+// BoolSlice gets the value as a []bool, returns the optionalDefault
+// value or nil if the value is not a []bool.
+func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool {
+	if s, ok := v.data.([]bool); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustBoolSlice gets the value as a []bool.
+//
+// Panics if the object is not a []bool.
+func (v *Value) MustBoolSlice() []bool {
+	return v.data.([]bool)
+}
+
+// IsBool gets whether the object contained is a bool or not.
+func (v *Value) IsBool() bool {
+	_, ok := v.data.(bool)
+	return ok
+}
+
+// IsBoolSlice gets whether the object contained is a []bool or not.
+func (v *Value) IsBoolSlice() bool {
+	_, ok := v.data.([]bool)
+	return ok
+}
+
+// EachBool calls the specified callback for each object
+// in the []bool.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachBool(callback func(int, bool) bool) *Value {
+
+	for index, val := range v.MustBoolSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereBool uses the specified decider function to select items
+// from the []bool.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereBool(decider func(int, bool) bool) *Value {
+
+	var selected []bool
+
+	v.EachBool(func(index int, val bool) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupBool uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]bool.
+func (v *Value) GroupBool(grouper func(int, bool) string) *Value {
+
+	groups := make(map[string][]bool)
+
+	v.EachBool(func(index int, val bool) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]bool, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceBool uses the specified function to replace each bools
+// by iterating each item.  The data in the returned result will be a
+// []bool containing the replaced items.
+func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value {
+
+	arr := v.MustBoolSlice()
+	replaced := make([]bool, len(arr))
+
+	v.EachBool(func(index int, val bool) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectBool uses the specified collector function to collect a value
+// for each of the bools in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value {
+
+	arr := v.MustBoolSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachBool(func(index int, val bool) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Str (string and []string)
+	--------------------------------------------------
+*/
+
+// Str gets the value as a string, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Str(optionalDefault ...string) string {
+	if s, ok := v.data.(string); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return ""
+}
+
+// MustStr gets the value as a string.
+//
+// Panics if the object is not a string.
+func (v *Value) MustStr() string {
+	return v.data.(string)
+}
+
+// StrSlice gets the value as a []string, returns the optionalDefault
+// value or nil if the value is not a []string.
+func (v *Value) StrSlice(optionalDefault ...[]string) []string {
+	if s, ok := v.data.([]string); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustStrSlice gets the value as a []string.
+//
+// Panics if the object is not a []string.
+func (v *Value) MustStrSlice() []string {
+	return v.data.([]string)
+}
+
+// IsStr gets whether the object contained is a string or not.
+func (v *Value) IsStr() bool {
+	_, ok := v.data.(string)
+	return ok
+}
+
+// IsStrSlice gets whether the object contained is a []string or not.
+func (v *Value) IsStrSlice() bool {
+	_, ok := v.data.([]string)
+	return ok
+}
+
+// EachStr calls the specified callback for each object
+// in the []string.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachStr(callback func(int, string) bool) *Value {
+
+	for index, val := range v.MustStrSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereStr uses the specified decider function to select items
+// from the []string.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereStr(decider func(int, string) bool) *Value {
+
+	var selected []string
+
+	v.EachStr(func(index int, val string) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupStr uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]string.
+func (v *Value) GroupStr(grouper func(int, string) string) *Value {
+
+	groups := make(map[string][]string)
+
+	v.EachStr(func(index int, val string) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]string, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceStr uses the specified function to replace each strings
+// by iterating each item.  The data in the returned result will be a
+// []string containing the replaced items.
+func (v *Value) ReplaceStr(replacer func(int, string) string) *Value {
+
+	arr := v.MustStrSlice()
+	replaced := make([]string, len(arr))
+
+	v.EachStr(func(index int, val string) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectStr uses the specified collector function to collect a value
+// for each of the strings in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectStr(collector func(int, string) interface{}) *Value {
+
+	arr := v.MustStrSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachStr(func(index int, val string) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Int (int and []int)
+	--------------------------------------------------
+*/
+
+// Int gets the value as a int, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int(optionalDefault ...int) int {
+	if s, ok := v.data.(int); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustInt gets the value as a int.
+//
+// Panics if the object is not a int.
+func (v *Value) MustInt() int {
+	return v.data.(int)
+}
+
+// IntSlice gets the value as a []int, returns the optionalDefault
+// value or nil if the value is not a []int.
+func (v *Value) IntSlice(optionalDefault ...[]int) []int {
+	if s, ok := v.data.([]int); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustIntSlice gets the value as a []int.
+//
+// Panics if the object is not a []int.
+func (v *Value) MustIntSlice() []int {
+	return v.data.([]int)
+}
+
+// IsInt gets whether the object contained is a int or not.
+func (v *Value) IsInt() bool {
+	_, ok := v.data.(int)
+	return ok
+}
+
+// IsIntSlice gets whether the object contained is a []int or not.
+func (v *Value) IsIntSlice() bool {
+	_, ok := v.data.([]int)
+	return ok
+}
+
+// EachInt calls the specified callback for each object
+// in the []int.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt(callback func(int, int) bool) *Value {
+
+	for index, val := range v.MustIntSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereInt uses the specified decider function to select items
+// from the []int.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt(decider func(int, int) bool) *Value {
+
+	var selected []int
+
+	v.EachInt(func(index int, val int) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupInt uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]int.
+func (v *Value) GroupInt(grouper func(int, int) string) *Value {
+
+	groups := make(map[string][]int)
+
+	v.EachInt(func(index int, val int) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]int, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceInt uses the specified function to replace each ints
+// by iterating each item.  The data in the returned result will be a
+// []int containing the replaced items.
+func (v *Value) ReplaceInt(replacer func(int, int) int) *Value {
+
+	arr := v.MustIntSlice()
+	replaced := make([]int, len(arr))
+
+	v.EachInt(func(index int, val int) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectInt uses the specified collector function to collect a value
+// for each of the ints in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt(collector func(int, int) interface{}) *Value {
+
+	arr := v.MustIntSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachInt(func(index int, val int) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Int8 (int8 and []int8)
+	--------------------------------------------------
+*/
+
+// Int8 gets the value as a int8, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int8(optionalDefault ...int8) int8 {
+	if s, ok := v.data.(int8); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustInt8 gets the value as a int8.
+//
+// Panics if the object is not a int8.
+func (v *Value) MustInt8() int8 {
+	return v.data.(int8)
+}
+
+// Int8Slice gets the value as a []int8, returns the optionalDefault
+// value or nil if the value is not a []int8.
+func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 {
+	if s, ok := v.data.([]int8); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustInt8Slice gets the value as a []int8.
+//
+// Panics if the object is not a []int8.
+func (v *Value) MustInt8Slice() []int8 {
+	return v.data.([]int8)
+}
+
+// IsInt8 gets whether the object contained is a int8 or not.
+func (v *Value) IsInt8() bool {
+	_, ok := v.data.(int8)
+	return ok
+}
+
+// IsInt8Slice gets whether the object contained is a []int8 or not.
+func (v *Value) IsInt8Slice() bool {
+	_, ok := v.data.([]int8)
+	return ok
+}
+
+// EachInt8 calls the specified callback for each object
+// in the []int8.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt8(callback func(int, int8) bool) *Value {
+
+	for index, val := range v.MustInt8Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereInt8 uses the specified decider function to select items
+// from the []int8.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt8(decider func(int, int8) bool) *Value {
+
+	var selected []int8
+
+	v.EachInt8(func(index int, val int8) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupInt8 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]int8.
+func (v *Value) GroupInt8(grouper func(int, int8) string) *Value {
+
+	groups := make(map[string][]int8)
+
+	v.EachInt8(func(index int, val int8) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]int8, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceInt8 uses the specified function to replace each int8s
+// by iterating each item.  The data in the returned result will be a
+// []int8 containing the replaced items.
+func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value {
+
+	arr := v.MustInt8Slice()
+	replaced := make([]int8, len(arr))
+
+	v.EachInt8(func(index int, val int8) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectInt8 uses the specified collector function to collect a value
+// for each of the int8s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value {
+
+	arr := v.MustInt8Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachInt8(func(index int, val int8) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Int16 (int16 and []int16)
+	--------------------------------------------------
+*/
+
+// Int16 gets the value as a int16, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int16(optionalDefault ...int16) int16 {
+	if s, ok := v.data.(int16); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustInt16 gets the value as a int16.
+//
+// Panics if the object is not a int16.
+func (v *Value) MustInt16() int16 {
+	return v.data.(int16)
+}
+
+// Int16Slice gets the value as a []int16, returns the optionalDefault
+// value or nil if the value is not a []int16.
+func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 {
+	if s, ok := v.data.([]int16); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustInt16Slice gets the value as a []int16.
+//
+// Panics if the object is not a []int16.
+func (v *Value) MustInt16Slice() []int16 {
+	return v.data.([]int16)
+}
+
+// IsInt16 gets whether the object contained is a int16 or not.
+func (v *Value) IsInt16() bool {
+	_, ok := v.data.(int16)
+	return ok
+}
+
+// IsInt16Slice gets whether the object contained is a []int16 or not.
+func (v *Value) IsInt16Slice() bool {
+	_, ok := v.data.([]int16)
+	return ok
+}
+
+// EachInt16 calls the specified callback for each object
+// in the []int16.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt16(callback func(int, int16) bool) *Value {
+
+	for index, val := range v.MustInt16Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereInt16 uses the specified decider function to select items
+// from the []int16.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt16(decider func(int, int16) bool) *Value {
+
+	var selected []int16
+
+	v.EachInt16(func(index int, val int16) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupInt16 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]int16.
+func (v *Value) GroupInt16(grouper func(int, int16) string) *Value {
+
+	groups := make(map[string][]int16)
+
+	v.EachInt16(func(index int, val int16) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]int16, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceInt16 uses the specified function to replace each int16s
+// by iterating each item.  The data in the returned result will be a
+// []int16 containing the replaced items.
+func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value {
+
+	arr := v.MustInt16Slice()
+	replaced := make([]int16, len(arr))
+
+	v.EachInt16(func(index int, val int16) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectInt16 uses the specified collector function to collect a value
+// for each of the int16s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value {
+
+	arr := v.MustInt16Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachInt16(func(index int, val int16) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Int32 (int32 and []int32)
+	--------------------------------------------------
+*/
+
+// Int32 gets the value as a int32, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int32(optionalDefault ...int32) int32 {
+	if s, ok := v.data.(int32); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustInt32 gets the value as a int32.
+//
+// Panics if the object is not a int32.
+func (v *Value) MustInt32() int32 {
+	return v.data.(int32)
+}
+
+// Int32Slice gets the value as a []int32, returns the optionalDefault
+// value or nil if the value is not a []int32.
+func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 {
+	if s, ok := v.data.([]int32); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustInt32Slice gets the value as a []int32.
+//
+// Panics if the object is not a []int32.
+func (v *Value) MustInt32Slice() []int32 {
+	return v.data.([]int32)
+}
+
+// IsInt32 gets whether the object contained is a int32 or not.
+func (v *Value) IsInt32() bool {
+	_, ok := v.data.(int32)
+	return ok
+}
+
+// IsInt32Slice gets whether the object contained is a []int32 or not.
+func (v *Value) IsInt32Slice() bool {
+	_, ok := v.data.([]int32)
+	return ok
+}
+
+// EachInt32 calls the specified callback for each object
+// in the []int32.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt32(callback func(int, int32) bool) *Value {
+
+	for index, val := range v.MustInt32Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereInt32 uses the specified decider function to select items
+// from the []int32.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt32(decider func(int, int32) bool) *Value {
+
+	var selected []int32
+
+	v.EachInt32(func(index int, val int32) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupInt32 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]int32.
+func (v *Value) GroupInt32(grouper func(int, int32) string) *Value {
+
+	groups := make(map[string][]int32)
+
+	v.EachInt32(func(index int, val int32) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]int32, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceInt32 uses the specified function to replace each int32s
+// by iterating each item.  The data in the returned result will be a
+// []int32 containing the replaced items.
+func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value {
+
+	arr := v.MustInt32Slice()
+	replaced := make([]int32, len(arr))
+
+	v.EachInt32(func(index int, val int32) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectInt32 uses the specified collector function to collect a value
+// for each of the int32s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value {
+
+	arr := v.MustInt32Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachInt32(func(index int, val int32) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Int64 (int64 and []int64)
+	--------------------------------------------------
+*/
+
+// Int64 gets the value as a int64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int64(optionalDefault ...int64) int64 {
+	if s, ok := v.data.(int64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustInt64 gets the value as a int64.
+//
+// Panics if the object is not a int64.
+func (v *Value) MustInt64() int64 {
+	return v.data.(int64)
+}
+
+// Int64Slice gets the value as a []int64, returns the optionalDefault
+// value or nil if the value is not a []int64.
+func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 {
+	if s, ok := v.data.([]int64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustInt64Slice gets the value as a []int64.
+//
+// Panics if the object is not a []int64.
+func (v *Value) MustInt64Slice() []int64 {
+	return v.data.([]int64)
+}
+
+// IsInt64 gets whether the object contained is a int64 or not.
+func (v *Value) IsInt64() bool {
+	_, ok := v.data.(int64)
+	return ok
+}
+
+// IsInt64Slice gets whether the object contained is a []int64 or not.
+func (v *Value) IsInt64Slice() bool {
+	_, ok := v.data.([]int64)
+	return ok
+}
+
+// EachInt64 calls the specified callback for each object
+// in the []int64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt64(callback func(int, int64) bool) *Value {
+
+	for index, val := range v.MustInt64Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereInt64 uses the specified decider function to select items
+// from the []int64.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt64(decider func(int, int64) bool) *Value {
+
+	var selected []int64
+
+	v.EachInt64(func(index int, val int64) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupInt64 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]int64.
+func (v *Value) GroupInt64(grouper func(int, int64) string) *Value {
+
+	groups := make(map[string][]int64)
+
+	v.EachInt64(func(index int, val int64) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]int64, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceInt64 uses the specified function to replace each int64s
+// by iterating each item.  The data in the returned result will be a
+// []int64 containing the replaced items.
+func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value {
+
+	arr := v.MustInt64Slice()
+	replaced := make([]int64, len(arr))
+
+	v.EachInt64(func(index int, val int64) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectInt64 uses the specified collector function to collect a value
+// for each of the int64s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value {
+
+	arr := v.MustInt64Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachInt64(func(index int, val int64) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Uint (uint and []uint)
+	--------------------------------------------------
+*/
+
+// Uint gets the value as a uint, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint(optionalDefault ...uint) uint {
+	if s, ok := v.data.(uint); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustUint gets the value as a uint.
+//
+// Panics if the object is not a uint.
+func (v *Value) MustUint() uint {
+	return v.data.(uint)
+}
+
+// UintSlice gets the value as a []uint, returns the optionalDefault
+// value or nil if the value is not a []uint.
+func (v *Value) UintSlice(optionalDefault ...[]uint) []uint {
+	if s, ok := v.data.([]uint); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustUintSlice gets the value as a []uint.
+//
+// Panics if the object is not a []uint.
+func (v *Value) MustUintSlice() []uint {
+	return v.data.([]uint)
+}
+
+// IsUint gets whether the object contained is a uint or not.
+func (v *Value) IsUint() bool {
+	_, ok := v.data.(uint)
+	return ok
+}
+
+// IsUintSlice gets whether the object contained is a []uint or not.
+func (v *Value) IsUintSlice() bool {
+	_, ok := v.data.([]uint)
+	return ok
+}
+
+// EachUint calls the specified callback for each object
+// in the []uint.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint(callback func(int, uint) bool) *Value {
+
+	for index, val := range v.MustUintSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereUint uses the specified decider function to select items
+// from the []uint.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint(decider func(int, uint) bool) *Value {
+
+	var selected []uint
+
+	v.EachUint(func(index int, val uint) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupUint uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]uint.
+func (v *Value) GroupUint(grouper func(int, uint) string) *Value {
+
+	groups := make(map[string][]uint)
+
+	v.EachUint(func(index int, val uint) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]uint, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceUint uses the specified function to replace each uints
+// by iterating each item.  The data in the returned result will be a
+// []uint containing the replaced items.
+func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value {
+
+	arr := v.MustUintSlice()
+	replaced := make([]uint, len(arr))
+
+	v.EachUint(func(index int, val uint) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectUint uses the specified collector function to collect a value
+// for each of the uints in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value {
+
+	arr := v.MustUintSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachUint(func(index int, val uint) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Uint8 (uint8 and []uint8)
+	--------------------------------------------------
+*/
+
+// Uint8 gets the value as a uint8, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint8(optionalDefault ...uint8) uint8 {
+	if s, ok := v.data.(uint8); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustUint8 gets the value as a uint8.
+//
+// Panics if the object is not a uint8.
+func (v *Value) MustUint8() uint8 {
+	return v.data.(uint8)
+}
+
+// Uint8Slice gets the value as a []uint8, returns the optionalDefault
+// value or nil if the value is not a []uint8.
+func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 {
+	if s, ok := v.data.([]uint8); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustUint8Slice gets the value as a []uint8.
+//
+// Panics if the object is not a []uint8.
+func (v *Value) MustUint8Slice() []uint8 {
+	return v.data.([]uint8)
+}
+
+// IsUint8 gets whether the object contained is a uint8 or not.
+func (v *Value) IsUint8() bool {
+	_, ok := v.data.(uint8)
+	return ok
+}
+
+// IsUint8Slice gets whether the object contained is a []uint8 or not.
+func (v *Value) IsUint8Slice() bool {
+	_, ok := v.data.([]uint8)
+	return ok
+}
+
+// EachUint8 calls the specified callback for each object
+// in the []uint8.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint8(callback func(int, uint8) bool) *Value {
+
+	for index, val := range v.MustUint8Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereUint8 uses the specified decider function to select items
+// from the []uint8.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value {
+
+	var selected []uint8
+
+	v.EachUint8(func(index int, val uint8) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupUint8 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]uint8.
+func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value {
+
+	groups := make(map[string][]uint8)
+
+	v.EachUint8(func(index int, val uint8) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]uint8, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceUint8 uses the specified function to replace each uint8s
+// by iterating each item.  The data in the returned result will be a
+// []uint8 containing the replaced items.
+func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value {
+
+	arr := v.MustUint8Slice()
+	replaced := make([]uint8, len(arr))
+
+	v.EachUint8(func(index int, val uint8) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectUint8 uses the specified collector function to collect a value
+// for each of the uint8s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value {
+
+	arr := v.MustUint8Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachUint8(func(index int, val uint8) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Uint16 (uint16 and []uint16)
+	--------------------------------------------------
+*/
+
+// Uint16 gets the value as a uint16, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint16(optionalDefault ...uint16) uint16 {
+	if s, ok := v.data.(uint16); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustUint16 gets the value as a uint16.
+//
+// Panics if the object is not a uint16.
+func (v *Value) MustUint16() uint16 {
+	return v.data.(uint16)
+}
+
+// Uint16Slice gets the value as a []uint16, returns the optionalDefault
+// value or nil if the value is not a []uint16.
+func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 {
+	if s, ok := v.data.([]uint16); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustUint16Slice gets the value as a []uint16.
+//
+// Panics if the object is not a []uint16.
+func (v *Value) MustUint16Slice() []uint16 {
+	return v.data.([]uint16)
+}
+
+// IsUint16 gets whether the object contained is a uint16 or not.
+func (v *Value) IsUint16() bool {
+	_, ok := v.data.(uint16)
+	return ok
+}
+
+// IsUint16Slice gets whether the object contained is a []uint16 or not.
+func (v *Value) IsUint16Slice() bool {
+	_, ok := v.data.([]uint16)
+	return ok
+}
+
+// EachUint16 calls the specified callback for each object
+// in the []uint16.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint16(callback func(int, uint16) bool) *Value {
+
+	for index, val := range v.MustUint16Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereUint16 uses the specified decider function to select items
+// from the []uint16.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value {
+
+	var selected []uint16
+
+	v.EachUint16(func(index int, val uint16) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupUint16 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]uint16.
+func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value {
+
+	groups := make(map[string][]uint16)
+
+	v.EachUint16(func(index int, val uint16) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]uint16, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceUint16 uses the specified function to replace each uint16s
+// by iterating each item.  The data in the returned result will be a
+// []uint16 containing the replaced items.
+func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value {
+
+	arr := v.MustUint16Slice()
+	replaced := make([]uint16, len(arr))
+
+	v.EachUint16(func(index int, val uint16) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectUint16 uses the specified collector function to collect a value
+// for each of the uint16s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value {
+
+	arr := v.MustUint16Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachUint16(func(index int, val uint16) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Uint32 (uint32 and []uint32)
+	--------------------------------------------------
+*/
+
+// Uint32 gets the value as a uint32, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint32(optionalDefault ...uint32) uint32 {
+	if s, ok := v.data.(uint32); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustUint32 gets the value as a uint32.
+//
+// Panics if the object is not a uint32.
+func (v *Value) MustUint32() uint32 {
+	return v.data.(uint32)
+}
+
+// Uint32Slice gets the value as a []uint32, returns the optionalDefault
+// value or nil if the value is not a []uint32.
+func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 {
+	if s, ok := v.data.([]uint32); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustUint32Slice gets the value as a []uint32.
+//
+// Panics if the object is not a []uint32.
+func (v *Value) MustUint32Slice() []uint32 {
+	return v.data.([]uint32)
+}
+
+// IsUint32 gets whether the object contained is a uint32 or not.
+func (v *Value) IsUint32() bool {
+	_, ok := v.data.(uint32)
+	return ok
+}
+
+// IsUint32Slice gets whether the object contained is a []uint32 or not.
+func (v *Value) IsUint32Slice() bool {
+	_, ok := v.data.([]uint32)
+	return ok
+}
+
+// EachUint32 calls the specified callback for each object
+// in the []uint32.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint32(callback func(int, uint32) bool) *Value {
+
+	for index, val := range v.MustUint32Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereUint32 uses the specified decider function to select items
+// from the []uint32.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value {
+
+	var selected []uint32
+
+	v.EachUint32(func(index int, val uint32) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupUint32 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]uint32.
+func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value {
+
+	groups := make(map[string][]uint32)
+
+	v.EachUint32(func(index int, val uint32) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]uint32, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceUint32 uses the specified function to replace each uint32s
+// by iterating each item.  The data in the returned result will be a
+// []uint32 containing the replaced items.
+func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value {
+
+	arr := v.MustUint32Slice()
+	replaced := make([]uint32, len(arr))
+
+	v.EachUint32(func(index int, val uint32) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectUint32 uses the specified collector function to collect a value
+// for each of the uint32s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value {
+
+	arr := v.MustUint32Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachUint32(func(index int, val uint32) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Uint64 (uint64 and []uint64)
+	--------------------------------------------------
+*/
+
+// Uint64 gets the value as a uint64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint64(optionalDefault ...uint64) uint64 {
+	if s, ok := v.data.(uint64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustUint64 gets the value as a uint64.
+//
+// Panics if the object is not a uint64.
+func (v *Value) MustUint64() uint64 {
+	return v.data.(uint64)
+}
+
+// Uint64Slice gets the value as a []uint64, returns the optionalDefault
+// value or nil if the value is not a []uint64.
+func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 {
+	if s, ok := v.data.([]uint64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustUint64Slice gets the value as a []uint64.
+//
+// Panics if the object is not a []uint64.
+func (v *Value) MustUint64Slice() []uint64 {
+	return v.data.([]uint64)
+}
+
+// IsUint64 gets whether the object contained is a uint64 or not.
+func (v *Value) IsUint64() bool {
+	_, ok := v.data.(uint64)
+	return ok
+}
+
+// IsUint64Slice gets whether the object contained is a []uint64 or not.
+func (v *Value) IsUint64Slice() bool {
+	_, ok := v.data.([]uint64)
+	return ok
+}
+
+// EachUint64 calls the specified callback for each object
+// in the []uint64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint64(callback func(int, uint64) bool) *Value {
+
+	for index, val := range v.MustUint64Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereUint64 uses the specified decider function to select items
+// from the []uint64.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value {
+
+	var selected []uint64
+
+	v.EachUint64(func(index int, val uint64) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupUint64 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]uint64.
+func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value {
+
+	groups := make(map[string][]uint64)
+
+	v.EachUint64(func(index int, val uint64) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]uint64, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceUint64 uses the specified function to replace each uint64s
+// by iterating each item.  The data in the returned result will be a
+// []uint64 containing the replaced items.
+func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value {
+
+	arr := v.MustUint64Slice()
+	replaced := make([]uint64, len(arr))
+
+	v.EachUint64(func(index int, val uint64) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectUint64 uses the specified collector function to collect a value
+// for each of the uint64s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value {
+
+	arr := v.MustUint64Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachUint64(func(index int, val uint64) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Uintptr (uintptr and []uintptr)
+	--------------------------------------------------
+*/
+
+// Uintptr gets the value as a uintptr, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr {
+	if s, ok := v.data.(uintptr); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustUintptr gets the value as a uintptr.
+//
+// Panics if the object is not a uintptr.
+func (v *Value) MustUintptr() uintptr {
+	return v.data.(uintptr)
+}
+
+// UintptrSlice gets the value as a []uintptr, returns the optionalDefault
+// value or nil if the value is not a []uintptr.
+func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr {
+	if s, ok := v.data.([]uintptr); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustUintptrSlice gets the value as a []uintptr.
+//
+// Panics if the object is not a []uintptr.
+func (v *Value) MustUintptrSlice() []uintptr {
+	return v.data.([]uintptr)
+}
+
+// IsUintptr gets whether the object contained is a uintptr or not.
+func (v *Value) IsUintptr() bool {
+	_, ok := v.data.(uintptr)
+	return ok
+}
+
+// IsUintptrSlice gets whether the object contained is a []uintptr or not.
+func (v *Value) IsUintptrSlice() bool {
+	_, ok := v.data.([]uintptr)
+	return ok
+}
+
+// EachUintptr calls the specified callback for each object
+// in the []uintptr.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value {
+
+	for index, val := range v.MustUintptrSlice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereUintptr uses the specified decider function to select items
+// from the []uintptr.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value {
+
+	var selected []uintptr
+
+	v.EachUintptr(func(index int, val uintptr) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupUintptr uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]uintptr.
+func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value {
+
+	groups := make(map[string][]uintptr)
+
+	v.EachUintptr(func(index int, val uintptr) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]uintptr, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceUintptr uses the specified function to replace each uintptrs
+// by iterating each item.  The data in the returned result will be a
+// []uintptr containing the replaced items.
+func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value {
+
+	arr := v.MustUintptrSlice()
+	replaced := make([]uintptr, len(arr))
+
+	v.EachUintptr(func(index int, val uintptr) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectUintptr uses the specified collector function to collect a value
+// for each of the uintptrs in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value {
+
+	arr := v.MustUintptrSlice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachUintptr(func(index int, val uintptr) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Float32 (float32 and []float32)
+	--------------------------------------------------
+*/
+
+// Float32 gets the value as a float32, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Float32(optionalDefault ...float32) float32 {
+	if s, ok := v.data.(float32); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustFloat32 gets the value as a float32.
+//
+// Panics if the object is not a float32.
+func (v *Value) MustFloat32() float32 {
+	return v.data.(float32)
+}
+
+// Float32Slice gets the value as a []float32, returns the optionalDefault
+// value or nil if the value is not a []float32.
+func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 {
+	if s, ok := v.data.([]float32); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustFloat32Slice gets the value as a []float32.
+//
+// Panics if the object is not a []float32.
+func (v *Value) MustFloat32Slice() []float32 {
+	return v.data.([]float32)
+}
+
+// IsFloat32 gets whether the object contained is a float32 or not.
+func (v *Value) IsFloat32() bool {
+	_, ok := v.data.(float32)
+	return ok
+}
+
+// IsFloat32Slice gets whether the object contained is a []float32 or not.
+func (v *Value) IsFloat32Slice() bool {
+	_, ok := v.data.([]float32)
+	return ok
+}
+
+// EachFloat32 calls the specified callback for each object
+// in the []float32.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachFloat32(callback func(int, float32) bool) *Value {
+
+	for index, val := range v.MustFloat32Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereFloat32 uses the specified decider function to select items
+// from the []float32.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value {
+
+	var selected []float32
+
+	v.EachFloat32(func(index int, val float32) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupFloat32 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]float32.
+func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value {
+
+	groups := make(map[string][]float32)
+
+	v.EachFloat32(func(index int, val float32) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]float32, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceFloat32 uses the specified function to replace each float32s
+// by iterating each item.  The data in the returned result will be a
+// []float32 containing the replaced items.
+func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value {
+
+	arr := v.MustFloat32Slice()
+	replaced := make([]float32, len(arr))
+
+	v.EachFloat32(func(index int, val float32) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectFloat32 uses the specified collector function to collect a value
+// for each of the float32s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value {
+
+	arr := v.MustFloat32Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachFloat32(func(index int, val float32) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Float64 (float64 and []float64)
+	--------------------------------------------------
+*/
+
+// Float64 gets the value as a float64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Float64(optionalDefault ...float64) float64 {
+	if s, ok := v.data.(float64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustFloat64 gets the value as a float64.
+//
+// Panics if the object is not a float64.
+func (v *Value) MustFloat64() float64 {
+	return v.data.(float64)
+}
+
+// Float64Slice gets the value as a []float64, returns the optionalDefault
+// value or nil if the value is not a []float64.
+func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 {
+	if s, ok := v.data.([]float64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustFloat64Slice gets the value as a []float64.
+//
+// Panics if the object is not a []float64.
+func (v *Value) MustFloat64Slice() []float64 {
+	return v.data.([]float64)
+}
+
+// IsFloat64 gets whether the object contained is a float64 or not.
+func (v *Value) IsFloat64() bool {
+	_, ok := v.data.(float64)
+	return ok
+}
+
+// IsFloat64Slice gets whether the object contained is a []float64 or not.
+func (v *Value) IsFloat64Slice() bool {
+	_, ok := v.data.([]float64)
+	return ok
+}
+
+// EachFloat64 calls the specified callback for each object
+// in the []float64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachFloat64(callback func(int, float64) bool) *Value {
+
+	for index, val := range v.MustFloat64Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereFloat64 uses the specified decider function to select items
+// from the []float64.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value {
+
+	var selected []float64
+
+	v.EachFloat64(func(index int, val float64) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupFloat64 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]float64.
+func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value {
+
+	groups := make(map[string][]float64)
+
+	v.EachFloat64(func(index int, val float64) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]float64, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceFloat64 uses the specified function to replace each float64s
+// by iterating each item.  The data in the returned result will be a
+// []float64 containing the replaced items.
+func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value {
+
+	arr := v.MustFloat64Slice()
+	replaced := make([]float64, len(arr))
+
+	v.EachFloat64(func(index int, val float64) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectFloat64 uses the specified collector function to collect a value
+// for each of the float64s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value {
+
+	arr := v.MustFloat64Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachFloat64(func(index int, val float64) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Complex64 (complex64 and []complex64)
+	--------------------------------------------------
+*/
+
+// Complex64 gets the value as a complex64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Complex64(optionalDefault ...complex64) complex64 {
+	if s, ok := v.data.(complex64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustComplex64 gets the value as a complex64.
+//
+// Panics if the object is not a complex64.
+func (v *Value) MustComplex64() complex64 {
+	return v.data.(complex64)
+}
+
+// Complex64Slice gets the value as a []complex64, returns the optionalDefault
+// value or nil if the value is not a []complex64.
+func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 {
+	if s, ok := v.data.([]complex64); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustComplex64Slice gets the value as a []complex64.
+//
+// Panics if the object is not a []complex64.
+func (v *Value) MustComplex64Slice() []complex64 {
+	return v.data.([]complex64)
+}
+
+// IsComplex64 gets whether the object contained is a complex64 or not.
+func (v *Value) IsComplex64() bool {
+	_, ok := v.data.(complex64)
+	return ok
+}
+
+// IsComplex64Slice gets whether the object contained is a []complex64 or not.
+func (v *Value) IsComplex64Slice() bool {
+	_, ok := v.data.([]complex64)
+	return ok
+}
+
+// EachComplex64 calls the specified callback for each object
+// in the []complex64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value {
+
+	for index, val := range v.MustComplex64Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereComplex64 uses the specified decider function to select items
+// from the []complex64.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value {
+
+	var selected []complex64
+
+	v.EachComplex64(func(index int, val complex64) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupComplex64 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]complex64.
+func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value {
+
+	groups := make(map[string][]complex64)
+
+	v.EachComplex64(func(index int, val complex64) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]complex64, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceComplex64 uses the specified function to replace each complex64s
+// by iterating each item.  The data in the returned result will be a
+// []complex64 containing the replaced items.
+func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value {
+
+	arr := v.MustComplex64Slice()
+	replaced := make([]complex64, len(arr))
+
+	v.EachComplex64(func(index int, val complex64) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectComplex64 uses the specified collector function to collect a value
+// for each of the complex64s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value {
+
+	arr := v.MustComplex64Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachComplex64(func(index int, val complex64) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}
+
+/*
+	Complex128 (complex128 and []complex128)
+	--------------------------------------------------
+*/
+
+// Complex128 gets the value as a complex128, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Complex128(optionalDefault ...complex128) complex128 {
+	if s, ok := v.data.(complex128); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return 0
+}
+
+// MustComplex128 gets the value as a complex128.
+//
+// Panics if the object is not a complex128.
+func (v *Value) MustComplex128() complex128 {
+	return v.data.(complex128)
+}
+
+// Complex128Slice gets the value as a []complex128, returns the optionalDefault
+// value or nil if the value is not a []complex128.
+func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 {
+	if s, ok := v.data.([]complex128); ok {
+		return s
+	}
+	if len(optionalDefault) == 1 {
+		return optionalDefault[0]
+	}
+	return nil
+}
+
+// MustComplex128Slice gets the value as a []complex128.
+//
+// Panics if the object is not a []complex128.
+func (v *Value) MustComplex128Slice() []complex128 {
+	return v.data.([]complex128)
+}
+
+// IsComplex128 gets whether the object contained is a complex128 or not.
+func (v *Value) IsComplex128() bool {
+	_, ok := v.data.(complex128)
+	return ok
+}
+
+// IsComplex128Slice gets whether the object contained is a []complex128 or not.
+func (v *Value) IsComplex128Slice() bool {
+	_, ok := v.data.([]complex128)
+	return ok
+}
+
+// EachComplex128 calls the specified callback for each object
+// in the []complex128.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value {
+
+	for index, val := range v.MustComplex128Slice() {
+		carryon := callback(index, val)
+		if carryon == false {
+			break
+		}
+	}
+
+	return v
+
+}
+
+// WhereComplex128 uses the specified decider function to select items
+// from the []complex128.  The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value {
+
+	var selected []complex128
+
+	v.EachComplex128(func(index int, val complex128) bool {
+		shouldSelect := decider(index, val)
+		if shouldSelect == false {
+			selected = append(selected, val)
+		}
+		return true
+	})
+
+	return &Value{data: selected}
+
+}
+
+// GroupComplex128 uses the specified grouper function to group the items
+// keyed by the return of the grouper.  The object contained in the
+// result will contain a map[string][]complex128.
+func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value {
+
+	groups := make(map[string][]complex128)
+
+	v.EachComplex128(func(index int, val complex128) bool {
+		group := grouper(index, val)
+		if _, ok := groups[group]; !ok {
+			groups[group] = make([]complex128, 0)
+		}
+		groups[group] = append(groups[group], val)
+		return true
+	})
+
+	return &Value{data: groups}
+
+}
+
+// ReplaceComplex128 uses the specified function to replace each complex128s
+// by iterating each item.  The data in the returned result will be a
+// []complex128 containing the replaced items.
+func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value {
+
+	arr := v.MustComplex128Slice()
+	replaced := make([]complex128, len(arr))
+
+	v.EachComplex128(func(index int, val complex128) bool {
+		replaced[index] = replacer(index, val)
+		return true
+	})
+
+	return &Value{data: replaced}
+
+}
+
+// CollectComplex128 uses the specified collector function to collect a value
+// for each of the complex128s in the slice.  The data returned will be a
+// []interface{}.
+func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value {
+
+	arr := v.MustComplex128Slice()
+	collected := make([]interface{}, len(arr))
+
+	v.EachComplex128(func(index int, val complex128) bool {
+		collected[index] = collector(index, val)
+		return true
+	})
+
+	return &Value{data: collected}
+}

+ 13 - 0
vendor/src/github.com/stretchr/objx/value.go

@@ -0,0 +1,13 @@
+package objx
+
+// Value provides methods for extracting interface{} data in various
+// types.
+type Value struct {
+	// data contains the raw data being managed by this Value
+	data interface{}
+}
+
+// Data returns the raw data contained by this Value
+func (v *Value) Data() interface{} {
+	return v.data
+}

+ 911 - 0
vendor/src/github.com/stretchr/testify/assert/assertions.go

@@ -0,0 +1,911 @@
+package assert
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"math"
+	"reflect"
+	"regexp"
+	"runtime"
+	"strings"
+	"time"
+	"unicode"
+	"unicode/utf8"
+)
+
+// TestingT is an interface wrapper around *testing.T
+type TestingT interface {
+	Errorf(format string, args ...interface{})
+}
+
+// Comparison a custom function that returns true on success and false on failure
+type Comparison func() (success bool)
+
+/*
+	Helper functions
+*/
+
+// ObjectsAreEqual determines if two objects are considered equal.
+//
+// This function does no assertion of any kind.
+func ObjectsAreEqual(expected, actual interface{}) bool {
+
+	if expected == nil || actual == nil {
+		return expected == actual
+	}
+
+	if reflect.DeepEqual(expected, actual) {
+		return true
+	}
+
+	return false
+
+}
+
+// ObjectsAreEqualValues gets whether two objects are equal, or if their
+// values are equal.
+func ObjectsAreEqualValues(expected, actual interface{}) bool {
+	if ObjectsAreEqual(expected, actual) {
+		return true
+	}
+
+	actualType := reflect.TypeOf(actual)
+	expectedValue := reflect.ValueOf(expected)
+	if expectedValue.Type().ConvertibleTo(actualType) {
+		// Attempt comparison after type conversion
+		if reflect.DeepEqual(expectedValue.Convert(actualType).Interface(), actual) {
+			return true
+		}
+	}
+
+	return false
+}
+
+/* CallerInfo is necessary because the assert functions use the testing object
+internally, causing it to print the file:line of the assert method, rather than where
+the problem actually occured in calling code.*/
+
+// CallerInfo returns an array of strings containing the file and line number
+// of each stack frame leading from the current test to the assert call that
+// failed.
+func CallerInfo() []string {
+
+	pc := uintptr(0)
+	file := ""
+	line := 0
+	ok := false
+	name := ""
+
+	callers := []string{}
+	for i := 0; ; i++ {
+		pc, file, line, ok = runtime.Caller(i)
+		if !ok {
+			return nil
+		}
+
+		// This is a huge edge case, but it will panic if this is the case, see #180
+		if file == "<autogenerated>" {
+			break
+		}
+
+		parts := strings.Split(file, "/")
+		dir := parts[len(parts)-2]
+		file = parts[len(parts)-1]
+		if (dir != "assert" && dir != "mock" && dir != "require") || file == "mock_test.go" {
+			callers = append(callers, fmt.Sprintf("%s:%d", file, line))
+		}
+
+		f := runtime.FuncForPC(pc)
+		if f == nil {
+			break
+		}
+		name = f.Name()
+		// Drop the package
+		segments := strings.Split(name, ".")
+		name = segments[len(segments)-1]
+		if isTest(name, "Test") ||
+			isTest(name, "Benchmark") ||
+			isTest(name, "Example") {
+			break
+		}
+	}
+
+	return callers
+}
+
+// Stolen from the `go test` tool.
+// isTest tells whether name looks like a test (or benchmark, according to prefix).
+// It is a Test (say) if there is a character after Test that is not a lower-case letter.
+// We don't want TesticularCancer.
+func isTest(name, prefix string) bool {
+	if !strings.HasPrefix(name, prefix) {
+		return false
+	}
+	if len(name) == len(prefix) { // "Test" is ok
+		return true
+	}
+	rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
+	return !unicode.IsLower(rune)
+}
+
+// getWhitespaceString returns a string that is long enough to overwrite the default
+// output from the go testing framework.
+func getWhitespaceString() string {
+
+	_, file, line, ok := runtime.Caller(1)
+	if !ok {
+		return ""
+	}
+	parts := strings.Split(file, "/")
+	file = parts[len(parts)-1]
+
+	return strings.Repeat(" ", len(fmt.Sprintf("%s:%d:      ", file, line)))
+
+}
+
+func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
+	if len(msgAndArgs) == 0 || msgAndArgs == nil {
+		return ""
+	}
+	if len(msgAndArgs) == 1 {
+		return msgAndArgs[0].(string)
+	}
+	if len(msgAndArgs) > 1 {
+		return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
+	}
+	return ""
+}
+
+// Indents all lines of the message by appending a number of tabs to each line, in an output format compatible with Go's
+// test printing (see inner comment for specifics)
+func indentMessageLines(message string, tabs int) string {
+	outBuf := new(bytes.Buffer)
+
+	for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
+		if i != 0 {
+			outBuf.WriteRune('\n')
+		}
+		for ii := 0; ii < tabs; ii++ {
+			outBuf.WriteRune('\t')
+			// Bizarrely, all lines except the first need one fewer tabs prepended, so deliberately advance the counter
+			// by 1 prematurely.
+			if ii == 0 && i > 0 {
+				ii++
+			}
+		}
+		outBuf.WriteString(scanner.Text())
+	}
+
+	return outBuf.String()
+}
+
+// Fail reports a failure through
+func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
+
+	message := messageFromMsgAndArgs(msgAndArgs...)
+
+	errorTrace := strings.Join(CallerInfo(), "\n\r\t\t\t")
+	if len(message) > 0 {
+		t.Errorf("\r%s\r\tError Trace:\t%s\n"+
+			"\r\tError:%s\n"+
+			"\r\tMessages:\t%s\n\r",
+			getWhitespaceString(),
+			errorTrace,
+			indentMessageLines(failureMessage, 2),
+			message)
+	} else {
+		t.Errorf("\r%s\r\tError Trace:\t%s\n"+
+			"\r\tError:%s\n\r",
+			getWhitespaceString(),
+			errorTrace,
+			indentMessageLines(failureMessage, 2))
+	}
+
+	return false
+}
+
+// Implements asserts that an object is implemented by the specified interface.
+//
+//    assert.Implements(t, (*MyInterface)(nil), new(MyObject), "MyObject")
+func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+
+	interfaceType := reflect.TypeOf(interfaceObject).Elem()
+
+	if !reflect.TypeOf(object).Implements(interfaceType) {
+		return Fail(t, fmt.Sprintf("Object must implement %v", interfaceType), msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// IsType asserts that the specified objects are of the same type.
+func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+
+	if !ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) {
+		return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %v", reflect.TypeOf(expectedType), reflect.TypeOf(object)), msgAndArgs...)
+	}
+
+	return true
+}
+
+// Equal asserts that two objects are equal.
+//
+//    assert.Equal(t, 123, 123, "123 and 123 should be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+
+	if !ObjectsAreEqual(expected, actual) {
+		return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
+			"        != %#v (actual)", expected, actual), msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// EqualValues asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+//    assert.EqualValues(t, uint32(123), int32(123), "123 and 123 should be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+
+	if !ObjectsAreEqualValues(expected, actual) {
+		return Fail(t, fmt.Sprintf("Not equal: %#v (expected)\n"+
+			"        != %#v (actual)", expected, actual), msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// Exactly asserts that two objects are equal is value and type.
+//
+//    assert.Exactly(t, int32(123), int64(123), "123 and 123 should NOT be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+
+	aType := reflect.TypeOf(expected)
+	bType := reflect.TypeOf(actual)
+
+	if aType != bType {
+		return Fail(t, "Types expected to match exactly", "%v != %v", aType, bType)
+	}
+
+	return Equal(t, expected, actual, msgAndArgs...)
+
+}
+
+// NotNil asserts that the specified object is not nil.
+//
+//    assert.NotNil(t, err, "err should be something")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+
+	success := true
+
+	if object == nil {
+		success = false
+	} else {
+		value := reflect.ValueOf(object)
+		kind := value.Kind()
+		if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
+			success = false
+		}
+	}
+
+	if !success {
+		Fail(t, "Expected value not to be nil.", msgAndArgs...)
+	}
+
+	return success
+}
+
+// isNil checks if a specified object is nil or not, without Failing.
+func isNil(object interface{}) bool {
+	if object == nil {
+		return true
+	}
+
+	value := reflect.ValueOf(object)
+	kind := value.Kind()
+	if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
+		return true
+	}
+
+	return false
+}
+
+// Nil asserts that the specified object is nil.
+//
+//    assert.Nil(t, err, "err should be nothing")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+	if isNil(object) {
+		return true
+	}
+	return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
+}
+
+var numericZeros = []interface{}{
+	int(0),
+	int8(0),
+	int16(0),
+	int32(0),
+	int64(0),
+	uint(0),
+	uint8(0),
+	uint16(0),
+	uint32(0),
+	uint64(0),
+	float32(0),
+	float64(0),
+}
+
+// isEmpty gets whether the specified object is considered empty or not.
+func isEmpty(object interface{}) bool {
+
+	if object == nil {
+		return true
+	} else if object == "" {
+		return true
+	} else if object == false {
+		return true
+	}
+
+	for _, v := range numericZeros {
+		if object == v {
+			return true
+		}
+	}
+
+	objValue := reflect.ValueOf(object)
+
+	switch objValue.Kind() {
+	case reflect.Map:
+		fallthrough
+	case reflect.Slice, reflect.Chan:
+		{
+			return (objValue.Len() == 0)
+		}
+	case reflect.Ptr:
+		{
+			switch object.(type) {
+			case *time.Time:
+				return object.(*time.Time).IsZero()
+			default:
+				return false
+			}
+		}
+	}
+	return false
+}
+
+// Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// assert.Empty(t, obj)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+
+	pass := isEmpty(object)
+	if !pass {
+		Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
+	}
+
+	return pass
+
+}
+
+// NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
+// a slice or a channel with len == 0.
+//
+// if assert.NotEmpty(t, obj) {
+//   assert.Equal(t, "two", obj[1])
+// }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
+
+	pass := !isEmpty(object)
+	if !pass {
+		Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
+	}
+
+	return pass
+
+}
+
+// getLen try to get length of object.
+// return (false, 0) if impossible.
+func getLen(x interface{}) (ok bool, length int) {
+	v := reflect.ValueOf(x)
+	defer func() {
+		if e := recover(); e != nil {
+			ok = false
+		}
+	}()
+	return true, v.Len()
+}
+
+// Len asserts that the specified object has specific length.
+// Len also fails if the object has a type that len() not accept.
+//
+//    assert.Len(t, mySlice, 3, "The size of slice is not 3")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool {
+	ok, l := getLen(object)
+	if !ok {
+		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", object), msgAndArgs...)
+	}
+
+	if l != length {
+		return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
+	}
+	return true
+}
+
+// True asserts that the specified value is true.
+//
+//    assert.True(t, myBool, "myBool should be true")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func True(t TestingT, value bool, msgAndArgs ...interface{}) bool {
+
+	if value != true {
+		return Fail(t, "Should be true", msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// False asserts that the specified value is true.
+//
+//    assert.False(t, myBool, "myBool should be false")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func False(t TestingT, value bool, msgAndArgs ...interface{}) bool {
+
+	if value != false {
+		return Fail(t, "Should be false", msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// NotEqual asserts that the specified values are NOT equal.
+//
+//    assert.NotEqual(t, obj1, obj2, "two objects shouldn't be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
+
+	if ObjectsAreEqual(expected, actual) {
+		return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// containsElement try loop over the list check if the list includes the element.
+// return (false, false) if impossible.
+// return (true, false) if element was not found.
+// return (true, true) if element was found.
+func includeElement(list interface{}, element interface{}) (ok, found bool) {
+
+	listValue := reflect.ValueOf(list)
+	elementValue := reflect.ValueOf(element)
+	defer func() {
+		if e := recover(); e != nil {
+			ok = false
+			found = false
+		}
+	}()
+
+	if reflect.TypeOf(list).Kind() == reflect.String {
+		return true, strings.Contains(listValue.String(), elementValue.String())
+	}
+
+	for i := 0; i < listValue.Len(); i++ {
+		if ObjectsAreEqual(listValue.Index(i).Interface(), element) {
+			return true, true
+		}
+	}
+	return true, false
+
+}
+
+// Contains asserts that the specified string or list(array, slice...) contains the
+// specified substring or element.
+//
+//    assert.Contains(t, "Hello World", "World", "But 'Hello World' does contain 'World'")
+//    assert.Contains(t, ["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
+
+	ok, found := includeElement(s, contains)
+	if !ok {
+		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
+	}
+	if !found {
+		return Fail(t, fmt.Sprintf("\"%s\" does not contain \"%s\"", s, contains), msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// NotContains asserts that the specified string or list(array, slice...) does NOT contain the
+// specified substring or element.
+//
+//    assert.NotContains(t, "Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
+//    assert.NotContains(t, ["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool {
+
+	ok, found := includeElement(s, contains)
+	if !ok {
+		return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...)
+	}
+	if found {
+		return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...)
+	}
+
+	return true
+
+}
+
+// Condition uses a Comparison to assert a complex condition.
+func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool {
+	result := comp()
+	if !result {
+		Fail(t, "Condition failed!", msgAndArgs...)
+	}
+	return result
+}
+
+// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics
+// methods, and represents a simple func that takes no arguments, and returns nothing.
+type PanicTestFunc func()
+
+// didPanic returns true if the function passed to it panics. Otherwise, it returns false.
+func didPanic(f PanicTestFunc) (bool, interface{}) {
+
+	didPanic := false
+	var message interface{}
+	func() {
+
+		defer func() {
+			if message = recover(); message != nil {
+				didPanic = true
+			}
+		}()
+
+		// call the target function
+		f()
+
+	}()
+
+	return didPanic, message
+
+}
+
+// Panics asserts that the code inside the specified PanicTestFunc panics.
+//
+//   assert.Panics(t, func(){
+//     GoCrazy()
+//   }, "Calling GoCrazy() should panic")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
+
+	if funcDidPanic, panicValue := didPanic(f); !funcDidPanic {
+		return Fail(t, fmt.Sprintf("func %#v should panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
+	}
+
+	return true
+}
+
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+//   assert.NotPanics(t, func(){
+//     RemainCalm()
+//   }, "Calling RemainCalm() should NOT panic")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool {
+
+	if funcDidPanic, panicValue := didPanic(f); funcDidPanic {
+		return Fail(t, fmt.Sprintf("func %#v should not panic\n\r\tPanic value:\t%v", f, panicValue), msgAndArgs...)
+	}
+
+	return true
+}
+
+// WithinDuration asserts that the two times are within duration delta of each other.
+//
+//   assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
+
+	dt := expected.Sub(actual)
+	if dt < -delta || dt > delta {
+		return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
+	}
+
+	return true
+}
+
+func toFloat(x interface{}) (float64, bool) {
+	var xf float64
+	xok := true
+
+	switch xn := x.(type) {
+	case uint8:
+		xf = float64(xn)
+	case uint16:
+		xf = float64(xn)
+	case uint32:
+		xf = float64(xn)
+	case uint64:
+		xf = float64(xn)
+	case int:
+		xf = float64(xn)
+	case int8:
+		xf = float64(xn)
+	case int16:
+		xf = float64(xn)
+	case int32:
+		xf = float64(xn)
+	case int64:
+		xf = float64(xn)
+	case float32:
+		xf = float64(xn)
+	case float64:
+		xf = float64(xn)
+	default:
+		xok = false
+	}
+
+	return xf, xok
+}
+
+// InDelta asserts that the two numerals are within delta of each other.
+//
+// 	 assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+
+	af, aok := toFloat(expected)
+	bf, bok := toFloat(actual)
+
+	if !aok || !bok {
+		return Fail(t, fmt.Sprintf("Parameters must be numerical"), msgAndArgs...)
+	}
+
+	if math.IsNaN(af) {
+		return Fail(t, fmt.Sprintf("Actual must not be NaN"), msgAndArgs...)
+	}
+
+	if math.IsNaN(bf) {
+		return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...)
+	}
+
+	dt := af - bf
+	if dt < -delta || dt > delta {
+		return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...)
+	}
+
+	return true
+}
+
+// InDeltaSlice is the same as InDelta, except it compares two slices.
+func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+	if expected == nil || actual == nil ||
+		reflect.TypeOf(actual).Kind() != reflect.Slice ||
+		reflect.TypeOf(expected).Kind() != reflect.Slice {
+		return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
+	}
+
+	actualSlice := reflect.ValueOf(actual)
+	expectedSlice := reflect.ValueOf(expected)
+
+	for i := 0; i < actualSlice.Len(); i++ {
+		result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta)
+		if !result {
+			return result
+		}
+	}
+
+	return true
+}
+
+// min(|expected|, |actual|) * epsilon
+func calcEpsilonDelta(expected, actual interface{}, epsilon float64) float64 {
+	af, aok := toFloat(expected)
+	bf, bok := toFloat(actual)
+
+	if !aok || !bok {
+		// invalid input
+		return 0
+	}
+
+	if af < 0 {
+		af = -af
+	}
+	if bf < 0 {
+		bf = -bf
+	}
+	var delta float64
+	if af < bf {
+		delta = af * epsilon
+	} else {
+		delta = bf * epsilon
+	}
+	return delta
+}
+
+// InEpsilon asserts that expected and actual have a relative error less than epsilon
+//
+// Returns whether the assertion was successful (true) or not (false).
+func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
+	delta := calcEpsilonDelta(expected, actual, epsilon)
+
+	return InDelta(t, expected, actual, delta, msgAndArgs...)
+}
+
+// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
+func InEpsilonSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+	if expected == nil || actual == nil ||
+		reflect.TypeOf(actual).Kind() != reflect.Slice ||
+		reflect.TypeOf(expected).Kind() != reflect.Slice {
+		return Fail(t, fmt.Sprintf("Parameters must be slice"), msgAndArgs...)
+	}
+
+	actualSlice := reflect.ValueOf(actual)
+	expectedSlice := reflect.ValueOf(expected)
+
+	for i := 0; i < actualSlice.Len(); i++ {
+		result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta)
+		if !result {
+			return result
+		}
+	}
+
+	return true
+}
+
+/*
+	Errors
+*/
+
+// NoError asserts that a function returned no error (i.e. `nil`).
+//
+//   actualObj, err := SomeFunction()
+//   if assert.NoError(t, err) {
+//	   assert.Equal(t, actualObj, expectedObj)
+//   }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
+	if isNil(err) {
+		return true
+	}
+
+	return Fail(t, fmt.Sprintf("No error is expected but got %v", err), msgAndArgs...)
+}
+
+// Error asserts that a function returned an error (i.e. not `nil`).
+//
+//   actualObj, err := SomeFunction()
+//   if assert.Error(t, err, "An error was expected") {
+//	   assert.Equal(t, err, expectedError)
+//   }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
+
+	message := messageFromMsgAndArgs(msgAndArgs...)
+	return NotNil(t, err, "An error is expected but got nil. %s", message)
+
+}
+
+// EqualError asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+//   actualObj, err := SomeFunction()
+//   if assert.Error(t, err, "An error was expected") {
+//	   assert.Equal(t, err, expectedError)
+//   }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool {
+
+	message := messageFromMsgAndArgs(msgAndArgs...)
+	if !NotNil(t, theError, "An error is expected but got nil. %s", message) {
+		return false
+	}
+	s := "An error with value \"%s\" is expected but got \"%s\". %s"
+	return Equal(t, errString, theError.Error(),
+		s, errString, theError.Error(), message)
+}
+
+// matchRegexp return true if a specified regexp matches a string.
+func matchRegexp(rx interface{}, str interface{}) bool {
+
+	var r *regexp.Regexp
+	if rr, ok := rx.(*regexp.Regexp); ok {
+		r = rr
+	} else {
+		r = regexp.MustCompile(fmt.Sprint(rx))
+	}
+
+	return (r.FindStringIndex(fmt.Sprint(str)) != nil)
+
+}
+
+// Regexp asserts that a specified regexp matches a string.
+//
+//  assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
+//  assert.Regexp(t, "start...$", "it's not starting")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+
+	match := matchRegexp(rx, str)
+
+	if !match {
+		Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...)
+	}
+
+	return match
+}
+
+// NotRegexp asserts that a specified regexp does not match a string.
+//
+//  assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
+//  assert.NotRegexp(t, "^start", "it's not starting")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+	match := matchRegexp(rx, str)
+
+	if match {
+		Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...)
+	}
+
+	return !match
+
+}
+
+// Zero asserts that i is the zero value for its type and returns the truth.
+func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
+	if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
+		return Fail(t, fmt.Sprintf("Should be zero, but was %v", i), msgAndArgs...)
+	}
+	return true
+}
+
+// NotZero asserts that i is not the zero value for its type and returns the truth.
+func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool {
+	if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) {
+		return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...)
+	}
+	return true
+}

+ 45 - 0
vendor/src/github.com/stretchr/testify/assert/doc.go

@@ -0,0 +1,45 @@
+// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
+//
+// Example Usage
+//
+// The following is a complete example using assert in a standard test function:
+//    import (
+//      "testing"
+//      "github.com/stretchr/testify/assert"
+//    )
+//
+//    func TestSomething(t *testing.T) {
+//
+//      var a string = "Hello"
+//      var b string = "Hello"
+//
+//      assert.Equal(t, a, b, "The two words should be the same.")
+//
+//    }
+//
+// if you assert many times, use the format below:
+//
+//    import (
+//      "testing"
+//      "github.com/stretchr/testify/assert"
+//    )
+//
+//    func TestSomething(t *testing.T) {
+//      assert := assert.New(t)
+//
+//      var a string = "Hello"
+//      var b string = "Hello"
+//
+//      assert.Equal(a, b, "The two words should be the same.")
+//    }
+//
+// Assertions
+//
+// Assertions allow you to easily write test code, and are global funcs in the `assert` package.
+// All assertion functions take, as the first argument, the `*testing.T` object provided by the
+// testing framework. This allows the assertion funcs to write the failings and other details to
+// the correct place.
+//
+// Every assertion function also takes an optional string message as the final argument,
+// allowing custom error messages to be appended to the message the assertion method outputs.
+package assert

+ 10 - 0
vendor/src/github.com/stretchr/testify/assert/errors.go

@@ -0,0 +1,10 @@
+package assert
+
+import (
+	"errors"
+)
+
+// AnError is an error instance useful for testing.  If the code does not care
+// about error specifics, and only needs to return the error for example, this
+// error should be used to make the test code more readable.
+var AnError = errors.New("assert.AnError general error for testing")

+ 275 - 0
vendor/src/github.com/stretchr/testify/assert/forward_assertions.go

@@ -0,0 +1,275 @@
+package assert
+
+import "time"
+
+// Assertions provides assertion methods around the
+// TestingT interface.
+type Assertions struct {
+	t TestingT
+}
+
+// New makes a new Assertions object for the specified TestingT.
+func New(t TestingT) *Assertions {
+	return &Assertions{
+		t: t,
+	}
+}
+
+// Fail reports a failure through
+func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
+	return Fail(a.t, failureMessage, msgAndArgs...)
+}
+
+// Implements asserts that an object is implemented by the specified interface.
+//
+//    assert.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
+func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+	return Implements(a.t, interfaceObject, object, msgAndArgs...)
+}
+
+// IsType asserts that the specified objects are of the same type.
+func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool {
+	return IsType(a.t, expectedType, object, msgAndArgs...)
+}
+
+// Equal asserts that two objects are equal.
+//
+//    assert.Equal(123, 123, "123 and 123 should be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Equal(expected, actual interface{}, msgAndArgs ...interface{}) bool {
+	return Equal(a.t, expected, actual, msgAndArgs...)
+}
+
+// EqualValues asserts that two objects are equal or convertable to the same types
+// and equal.
+//
+//    assert.EqualValues(uint32(123), int32(123), "123 and 123 should be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) EqualValues(expected, actual interface{}, msgAndArgs ...interface{}) bool {
+	return EqualValues(a.t, expected, actual, msgAndArgs...)
+}
+
+// Exactly asserts that two objects are equal is value and type.
+//
+//    assert.Exactly(int32(123), int64(123), "123 and 123 should NOT be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Exactly(expected, actual interface{}, msgAndArgs ...interface{}) bool {
+	return Exactly(a.t, expected, actual, msgAndArgs...)
+}
+
+// NotNil asserts that the specified object is not nil.
+//
+//    assert.NotNil(err, "err should be something")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool {
+	return NotNil(a.t, object, msgAndArgs...)
+}
+
+// Nil asserts that the specified object is nil.
+//
+//    assert.Nil(err, "err should be nothing")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool {
+	return Nil(a.t, object, msgAndArgs...)
+}
+
+// Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or a
+// slice with len == 0.
+//
+// assert.Empty(obj)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
+	return Empty(a.t, object, msgAndArgs...)
+}
+
+// NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or a
+// slice with len == 0.
+//
+// if assert.NotEmpty(obj) {
+//   assert.Equal("two", obj[1])
+// }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool {
+	return NotEmpty(a.t, object, msgAndArgs...)
+}
+
+// Len asserts that the specified object has specific length.
+// Len also fails if the object has a type that len() not accept.
+//
+//    assert.Len(mySlice, 3, "The size of slice is not 3")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool {
+	return Len(a.t, object, length, msgAndArgs...)
+}
+
+// True asserts that the specified value is true.
+//
+//    assert.True(myBool, "myBool should be true")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool {
+	return True(a.t, value, msgAndArgs...)
+}
+
+// False asserts that the specified value is true.
+//
+//    assert.False(myBool, "myBool should be false")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool {
+	return False(a.t, value, msgAndArgs...)
+}
+
+// NotEqual asserts that the specified values are NOT equal.
+//
+//    assert.NotEqual(obj1, obj2, "two objects shouldn't be equal")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NotEqual(expected, actual interface{}, msgAndArgs ...interface{}) bool {
+	return NotEqual(a.t, expected, actual, msgAndArgs...)
+}
+
+// Contains asserts that the specified string contains the specified substring.
+//
+//    assert.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Contains(s, contains interface{}, msgAndArgs ...interface{}) bool {
+	return Contains(a.t, s, contains, msgAndArgs...)
+}
+
+// NotContains asserts that the specified string does NOT contain the specified substring.
+//
+//    assert.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NotContains(s, contains interface{}, msgAndArgs ...interface{}) bool {
+	return NotContains(a.t, s, contains, msgAndArgs...)
+}
+
+// Condition uses a Comparison to assert a complex condition.
+func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
+	return Condition(a.t, comp, msgAndArgs...)
+}
+
+// Panics asserts that the code inside the specified PanicTestFunc panics.
+//
+//   assert.Panics(func(){
+//     GoCrazy()
+//   }, "Calling GoCrazy() should panic")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
+	return Panics(a.t, f, msgAndArgs...)
+}
+
+// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
+//
+//   assert.NotPanics(func(){
+//     RemainCalm()
+//   }, "Calling RemainCalm() should NOT panic")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool {
+	return NotPanics(a.t, f, msgAndArgs...)
+}
+
+// WithinDuration asserts that the two times are within duration delta of each other.
+//
+//   assert.WithinDuration(time.Now(), time.Now(), 10*time.Second, "The difference should not be more than 10s")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) WithinDuration(expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
+	return WithinDuration(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InDelta asserts that the two numerals are within delta of each other.
+//
+// 	 assert.InDelta(t, math.Pi, (22 / 7.0), 0.01)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) InDelta(expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
+	return InDelta(a.t, expected, actual, delta, msgAndArgs...)
+}
+
+// InEpsilon asserts that expected and actual have a relative error less than epsilon
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) InEpsilon(expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
+	return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
+}
+
+// NoError asserts that a function returned no error (i.e. `nil`).
+//
+//   actualObj, err := SomeFunction()
+//   if assert.NoError(err) {
+//	   assert.Equal(actualObj, expectedObj)
+//   }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NoError(theError error, msgAndArgs ...interface{}) bool {
+	return NoError(a.t, theError, msgAndArgs...)
+}
+
+// Error asserts that a function returned an error (i.e. not `nil`).
+//
+//   actualObj, err := SomeFunction()
+//   if assert.Error(err, "An error was expected") {
+//	   assert.Equal(err, expectedError)
+//   }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Error(theError error, msgAndArgs ...interface{}) bool {
+	return Error(a.t, theError, msgAndArgs...)
+}
+
+// EqualError asserts that a function returned an error (i.e. not `nil`)
+// and that it is equal to the provided error.
+//
+//   actualObj, err := SomeFunction()
+//   if assert.Error(err, "An error was expected") {
+//	   assert.Equal(err, expectedError)
+//   }
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool {
+	return EqualError(a.t, theError, errString, msgAndArgs...)
+}
+
+// Regexp asserts that a specified regexp matches a string.
+//
+//  assert.Regexp(t, regexp.MustCompile("start"), "it's starting")
+//  assert.Regexp(t, "start...$", "it's not starting")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+	return Regexp(a.t, rx, str, msgAndArgs...)
+}
+
+// NotRegexp asserts that a specified regexp does not match a string.
+//
+//  assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting")
+//  assert.NotRegexp(t, "^start", "it's not starting")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool {
+	return NotRegexp(a.t, rx, str, msgAndArgs...)
+}
+
+// Zero asserts that i is the zero value for its type and returns the truth.
+func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool {
+	return Zero(a.t, i, msgAndArgs...)
+}
+
+// NotZero asserts that i is not the zero value for its type and returns the truth.
+func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool {
+	return NotZero(a.t, i, msgAndArgs...)
+}

+ 157 - 0
vendor/src/github.com/stretchr/testify/assert/http_assertions.go

@@ -0,0 +1,157 @@
+package assert
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"strings"
+)
+
+// httpCode is a helper that returns HTTP code of the response. It returns -1
+// if building a new request fails.
+func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int {
+	w := httptest.NewRecorder()
+	req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
+	if err != nil {
+		return -1
+	}
+	handler(w, req)
+	return w.Code
+}
+
+// HTTPSuccess asserts that a specified handler returns a success status code.
+//
+//  assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
+	code := httpCode(handler, method, url, values)
+	if code == -1 {
+		return false
+	}
+	return code >= http.StatusOK && code <= http.StatusPartialContent
+}
+
+// HTTPRedirect asserts that a specified handler returns a redirect status code.
+//
+//  assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
+	code := httpCode(handler, method, url, values)
+	if code == -1 {
+		return false
+	}
+	return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect
+}
+
+// HTTPError asserts that a specified handler returns an error status code.
+//
+//  assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool {
+	code := httpCode(handler, method, url, values)
+	if code == -1 {
+		return false
+	}
+	return code >= http.StatusBadRequest
+}
+
+// HTTPBody is a helper that returns HTTP body of the response. It returns
+// empty string if building a new request fails.
+func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string {
+	w := httptest.NewRecorder()
+	req, err := http.NewRequest(method, url+"?"+values.Encode(), nil)
+	if err != nil {
+		return ""
+	}
+	handler(w, req)
+	return w.Body.String()
+}
+
+// HTTPBodyContains asserts that a specified handler returns a
+// body that contains a string.
+//
+//  assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
+	body := HTTPBody(handler, method, url, values)
+
+	contains := strings.Contains(body, fmt.Sprint(str))
+	if !contains {
+		Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body))
+	}
+
+	return contains
+}
+
+// HTTPBodyNotContains asserts that a specified handler returns a
+// body that does not contain a string.
+//
+//  assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
+	body := HTTPBody(handler, method, url, values)
+
+	contains := strings.Contains(body, fmt.Sprint(str))
+	if contains {
+		Fail(t, "Expected response body for %s to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)
+	}
+
+	return !contains
+}
+
+//
+// Assertions Wrappers
+//
+
+// HTTPSuccess asserts that a specified handler returns a success status code.
+//
+//  assert.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil)
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method, url string, values url.Values) bool {
+	return HTTPSuccess(a.t, handler, method, url, values)
+}
+
+// HTTPRedirect asserts that a specified handler returns a redirect status code.
+//
+//  assert.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method, url string, values url.Values) bool {
+	return HTTPRedirect(a.t, handler, method, url, values)
+}
+
+// HTTPError asserts that a specified handler returns an error status code.
+//
+//  assert.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}}
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPError(handler http.HandlerFunc, method, url string, values url.Values) bool {
+	return HTTPError(a.t, handler, method, url, values)
+}
+
+// HTTPBodyContains asserts that a specified handler returns a
+// body that contains a string.
+//
+//  assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
+	return HTTPBodyContains(a.t, handler, method, url, values, str)
+}
+
+// HTTPBodyNotContains asserts that a specified handler returns a
+// body that does not contain a string.
+//
+//  assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky")
+//
+// Returns whether the assertion was successful (true) or not (false).
+func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool {
+	return HTTPBodyNotContains(a.t, handler, method, url, values, str)
+}

+ 43 - 0
vendor/src/github.com/stretchr/testify/mock/doc.go

@@ -0,0 +1,43 @@
+// Provides a system by which it is possible to mock your objects and verify calls are happening as expected.
+//
+// Example Usage
+//
+// The mock package provides an object, Mock, that tracks activity on another object.  It is usually
+// embedded into a test object as shown below:
+//
+//   type MyTestObject struct {
+//     // add a Mock object instance
+//     mock.Mock
+//
+//     // other fields go here as normal
+//   }
+//
+// When implementing the methods of an interface, you wire your functions up
+// to call the Mock.Called(args...) method, and return the appropriate values.
+//
+// For example, to mock a method that saves the name and age of a person and returns
+// the year of their birth or an error, you might write this:
+//
+//     func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) {
+//       args := o.Called(firstname, lastname, age)
+//       return args.Int(0), args.Error(1)
+//     }
+//
+// The Int, Error and Bool methods are examples of strongly typed getters that take the argument
+// index position. Given this argument list:
+//
+//     (12, true, "Something")
+//
+// You could read them out strongly typed like this:
+//
+//     args.Int(0)
+//     args.Bool(1)
+//     args.String(2)
+//
+// For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion:
+//
+//     return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine)
+//
+// This may cause a panic if the object you are getting is nil (the type assertion will fail), in those
+// cases you should check for nil first.
+package mock

+ 594 - 0
vendor/src/github.com/stretchr/testify/mock/mock.go

@@ -0,0 +1,594 @@
+package mock
+
+import (
+	"fmt"
+	"reflect"
+	"runtime"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/stretchr/objx"
+	"github.com/stretchr/testify/assert"
+)
+
+// TestingT is an interface wrapper around *testing.T
+type TestingT interface {
+	Logf(format string, args ...interface{})
+	Errorf(format string, args ...interface{})
+}
+
+/*
+	Call
+*/
+
+// Call represents a method call and is used for setting expectations,
+// as well as recording activity.
+type Call struct {
+
+	// The name of the method that was or will be called.
+	Method string
+
+	// Holds the arguments of the method.
+	Arguments Arguments
+
+	// Holds the arguments that should be returned when
+	// this method is called.
+	ReturnArguments Arguments
+
+	// The number of times to return the return arguments when setting
+	// expectations. 0 means to always return the value.
+	Repeatability int
+
+	// Holds a channel that will be used to block the Return until it either
+	// recieves a message or is closed. nil means it returns immediately.
+	WaitFor <-chan time.Time
+
+	// Holds a handler used to manipulate arguments content that are passed by
+	// reference. It's useful when mocking methods such as unmarshalers or
+	// decoders.
+	Run func(Arguments)
+}
+
+// Mock is the workhorse used to track activity on another object.
+// For an example of its usage, refer to the "Example Usage" section at the top of this document.
+type Mock struct {
+
+	// The method name that is currently
+	// being referred to by the On method.
+	onMethodName string
+
+	// An array of the arguments that are
+	// currently being referred to by the On method.
+	onMethodArguments Arguments
+
+	// Represents the calls that are expected of
+	// an object.
+	ExpectedCalls []Call
+
+	// Holds the calls that were made to this mocked object.
+	Calls []Call
+
+	// TestData holds any data that might be useful for testing.  Testify ignores
+	// this data completely allowing you to do whatever you like with it.
+	testData objx.Map
+
+	mutex sync.Mutex
+}
+
+// TestData holds any data that might be useful for testing.  Testify ignores
+// this data completely allowing you to do whatever you like with it.
+func (m *Mock) TestData() objx.Map {
+
+	if m.testData == nil {
+		m.testData = make(objx.Map)
+	}
+
+	return m.testData
+}
+
+/*
+	Setting expectations
+*/
+
+// On starts a description of an expectation of the specified method
+// being called.
+//
+//     Mock.On("MyMethod", arg1, arg2)
+func (m *Mock) On(methodName string, arguments ...interface{}) *Mock {
+	m.onMethodName = methodName
+	m.onMethodArguments = arguments
+
+	for _, arg := range arguments {
+		if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
+			panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
+		}
+	}
+
+	return m
+}
+
+// Return finishes a description of an expectation of the method (and arguments)
+// specified in the most recent On method call.
+//
+//     Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2)
+func (m *Mock) Return(returnArguments ...interface{}) *Mock {
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+
+	m.ExpectedCalls = append(m.ExpectedCalls, Call{m.onMethodName, m.onMethodArguments, returnArguments, 0, nil, nil})
+	return m
+}
+
+// Once indicates that that the mock should only return the value once.
+//
+//    Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
+func (m *Mock) Once() {
+	m.mutex.Lock()
+	m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 1
+	m.mutex.Unlock()
+}
+
+// Twice indicates that that the mock should only return the value twice.
+//
+//    Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
+func (m *Mock) Twice() {
+	m.mutex.Lock()
+	m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = 2
+	m.mutex.Unlock()
+}
+
+// Times indicates that that the mock should only return the indicated number
+// of times.
+//
+//    Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
+func (m *Mock) Times(i int) {
+	m.mutex.Lock()
+	m.ExpectedCalls[len(m.ExpectedCalls)-1].Repeatability = i
+	m.mutex.Unlock()
+}
+
+// WaitUntil sets the channel that will block the mock's return until its closed
+// or a message is received.
+//
+//    Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
+func (m *Mock) WaitUntil(w <-chan time.Time) *Mock {
+	m.mutex.Lock()
+	m.ExpectedCalls[len(m.ExpectedCalls)-1].WaitFor = w
+	m.mutex.Unlock()
+	return m
+}
+
+// After sets how long to block until the call returns
+//
+//    Mock.On("MyMethod", arg1, arg2).After(time.Second)
+func (m *Mock) After(d time.Duration) *Mock {
+	return m.WaitUntil(time.After(d))
+}
+
+// Run sets a handler to be called before returning. It can be used when
+// mocking a method such as unmarshalers that takes a pointer to a struct and
+// sets properties in such struct
+//
+//    Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}").Return().Run(function(args Arguments) {
+//    	arg := args.Get(0).(*map[string]interface{})
+//    	arg["foo"] = "bar"
+//    })
+func (m *Mock) Run(fn func(Arguments)) *Mock {
+	m.mutex.Lock()
+	m.ExpectedCalls[len(m.ExpectedCalls)-1].Run = fn
+	m.mutex.Unlock()
+	return m
+}
+
+/*
+	Recording and responding to activity
+*/
+
+func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *Call) {
+	for i, call := range m.expectedCalls() {
+		if call.Method == method && call.Repeatability > -1 {
+
+			_, diffCount := call.Arguments.Diff(arguments)
+			if diffCount == 0 {
+				return i, &call
+			}
+
+		}
+	}
+	return -1, nil
+}
+
+func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *Call) {
+	diffCount := 0
+	var closestCall *Call = nil
+
+	for _, call := range m.expectedCalls() {
+		if call.Method == method {
+
+			_, tempDiffCount := call.Arguments.Diff(arguments)
+			if tempDiffCount < diffCount || diffCount == 0 {
+				diffCount = tempDiffCount
+				closestCall = &call
+			}
+
+		}
+	}
+
+	if closestCall == nil {
+		return false, nil
+	}
+
+	return true, closestCall
+}
+
+func callString(method string, arguments Arguments, includeArgumentValues bool) string {
+
+	var argValsString string = ""
+	if includeArgumentValues {
+		var argVals []string
+		for argIndex, arg := range arguments {
+			argVals = append(argVals, fmt.Sprintf("%d: %v", argIndex, arg))
+		}
+		argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t"))
+	}
+
+	return fmt.Sprintf("%s(%s)%s", method, arguments.String(), argValsString)
+}
+
+// Called tells the mock object that a method has been called, and gets an array
+// of arguments to return.  Panics if the call is unexpected (i.e. not preceeded by
+// appropriate .On .Return() calls)
+// If Call.WaitFor is set, blocks until the channel is closed or receives a message.
+func (m *Mock) Called(arguments ...interface{}) Arguments {
+	// get the calling function's name
+	pc, _, _, ok := runtime.Caller(1)
+	if !ok {
+		panic("Couldn't get the caller information")
+	}
+	functionPath := runtime.FuncForPC(pc).Name()
+	parts := strings.Split(functionPath, ".")
+	functionName := parts[len(parts)-1]
+
+	found, call := m.findExpectedCall(functionName, arguments...)
+
+	if found < 0 {
+		// we have to fail here - because we don't know what to do
+		// as the return arguments.  This is because:
+		//
+		//   a) this is a totally unexpected call to this method,
+		//   b) the arguments are not what was expected, or
+		//   c) the developer has forgotten to add an accompanying On...Return pair.
+
+		closestFound, closestCall := m.findClosestCall(functionName, arguments...)
+
+		if closestFound {
+			panic(fmt.Sprintf("\n\nmock: Unexpected Method Call\n-----------------------------\n\n%s\n\nThe closest call I have is: \n\n%s\n", callString(functionName, arguments, true), callString(functionName, closestCall.Arguments, true)))
+		} else {
+			panic(fmt.Sprintf("\nassert: mock: I don't know what to return because the method call was unexpected.\n\tEither do Mock.On(\"%s\").Return(...) first, or remove the %s() call.\n\tThis method was unexpected:\n\t\t%s\n\tat: %s", functionName, functionName, callString(functionName, arguments, true), assert.CallerInfo()))
+		}
+	} else {
+		m.mutex.Lock()
+		switch {
+		case call.Repeatability == 1:
+			call.Repeatability = -1
+			m.ExpectedCalls[found] = *call
+		case call.Repeatability > 1:
+			call.Repeatability -= 1
+			m.ExpectedCalls[found] = *call
+		}
+		m.mutex.Unlock()
+	}
+
+	// add the call
+	m.mutex.Lock()
+	m.Calls = append(m.Calls, Call{functionName, arguments, make([]interface{}, 0), 0, nil, nil})
+	m.mutex.Unlock()
+
+	// block if specified
+	if call.WaitFor != nil {
+		<-call.WaitFor
+	}
+
+	if call.Run != nil {
+		call.Run(arguments)
+	}
+
+	return call.ReturnArguments
+
+}
+
+/*
+	Assertions
+*/
+
+// AssertExpectationsForObjects asserts that everything specified with On and Return
+// of the specified objects was in fact called as expected.
+//
+// Calls may have occurred in any order.
+func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
+	var success bool = true
+	for _, obj := range testObjects {
+		mockObj := obj.(Mock)
+		success = success && mockObj.AssertExpectations(t)
+	}
+	return success
+}
+
+// AssertExpectations asserts that everything specified with On and Return was
+// in fact called as expected.  Calls may have occurred in any order.
+func (m *Mock) AssertExpectations(t TestingT) bool {
+	var somethingMissing bool = false
+	var failedExpectations int = 0
+
+	// iterate through each expectation
+	expectedCalls := m.expectedCalls()
+	for _, expectedCall := range expectedCalls {
+		switch {
+		case !m.methodWasCalled(expectedCall.Method, expectedCall.Arguments):
+			somethingMissing = true
+			failedExpectations++
+			t.Logf("\u274C\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
+		case expectedCall.Repeatability > 0:
+			somethingMissing = true
+			failedExpectations++
+		default:
+			t.Logf("\u2705\t%s(%s)", expectedCall.Method, expectedCall.Arguments.String())
+		}
+	}
+
+	if somethingMissing {
+		t.Errorf("FAIL: %d out of %d expectation(s) were met.\n\tThe code you are testing needs to make %d more call(s).\n\tat: %s", len(expectedCalls)-failedExpectations, len(expectedCalls), failedExpectations, assert.CallerInfo())
+	}
+
+	return !somethingMissing
+}
+
+// AssertNumberOfCalls asserts that the method was called expectedCalls times.
+func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool {
+	var actualCalls int = 0
+	for _, call := range m.calls() {
+		if call.Method == methodName {
+			actualCalls++
+		}
+	}
+	return assert.Equal(t, expectedCalls, actualCalls, fmt.Sprintf("Expected number of calls (%d) does not match the actual number of calls (%d).", expectedCalls, actualCalls))
+}
+
+// AssertCalled asserts that the method was called.
+func (m *Mock) AssertCalled(t TestingT, methodName string, arguments ...interface{}) bool {
+	if !assert.True(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method should have been called with %d argument(s), but was not.", methodName, len(arguments))) {
+		t.Logf("%v", m.expectedCalls())
+		return false
+	}
+	return true
+}
+
+// AssertNotCalled asserts that the method was not called.
+func (m *Mock) AssertNotCalled(t TestingT, methodName string, arguments ...interface{}) bool {
+	if !assert.False(t, m.methodWasCalled(methodName, arguments), fmt.Sprintf("The \"%s\" method was called with %d argument(s), but should NOT have been.", methodName, len(arguments))) {
+		t.Logf("%v", m.expectedCalls())
+		return false
+	}
+	return true
+}
+
+func (m *Mock) methodWasCalled(methodName string, expected []interface{}) bool {
+	for _, call := range m.calls() {
+		if call.Method == methodName {
+
+			_, differences := Arguments(expected).Diff(call.Arguments)
+
+			if differences == 0 {
+				// found the expected call
+				return true
+			}
+
+		}
+	}
+	// we didn't find the expected call
+	return false
+}
+
+func (m *Mock) expectedCalls() []Call {
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+	return append([]Call{}, m.ExpectedCalls...)
+}
+
+func (m *Mock) calls() []Call {
+	m.mutex.Lock()
+	defer m.mutex.Unlock()
+	return append([]Call{}, m.Calls...)
+}
+
+/*
+	Arguments
+*/
+
+// Arguments holds an array of method arguments or return values.
+type Arguments []interface{}
+
+const (
+	// The "any" argument.  Used in Diff and Assert when
+	// the argument being tested shouldn't be taken into consideration.
+	Anything string = "mock.Anything"
+)
+
+// AnythingOfTypeArgument is a string that contains the type of an argument
+// for use when type checking.  Used in Diff and Assert.
+type AnythingOfTypeArgument string
+
+// AnythingOfType returns an AnythingOfTypeArgument object containing the
+// name of the type to check for.  Used in Diff and Assert.
+//
+// For example:
+//	Assert(t, AnythingOfType("string"), AnythingOfType("int"))
+func AnythingOfType(t string) AnythingOfTypeArgument {
+	return AnythingOfTypeArgument(t)
+}
+
+// Get Returns the argument at the specified index.
+func (args Arguments) Get(index int) interface{} {
+	if index+1 > len(args) {
+		panic(fmt.Sprintf("assert: arguments: Cannot call Get(%d) because there are %d argument(s).", index, len(args)))
+	}
+	return args[index]
+}
+
+// Is gets whether the objects match the arguments specified.
+func (args Arguments) Is(objects ...interface{}) bool {
+	for i, obj := range args {
+		if obj != objects[i] {
+			return false
+		}
+	}
+	return true
+}
+
+// Diff gets a string describing the differences between the arguments
+// and the specified objects.
+//
+// Returns the diff string and number of differences found.
+func (args Arguments) Diff(objects []interface{}) (string, int) {
+
+	var output string = "\n"
+	var differences int
+
+	var maxArgCount int = len(args)
+	if len(objects) > maxArgCount {
+		maxArgCount = len(objects)
+	}
+
+	for i := 0; i < maxArgCount; i++ {
+		var actual, expected interface{}
+
+		if len(objects) <= i {
+			actual = "(Missing)"
+		} else {
+			actual = objects[i]
+		}
+
+		if len(args) <= i {
+			expected = "(Missing)"
+		} else {
+			expected = args[i]
+		}
+
+		if reflect.TypeOf(expected) == reflect.TypeOf((*AnythingOfTypeArgument)(nil)).Elem() {
+
+			// type checking
+			if reflect.TypeOf(actual).Name() != string(expected.(AnythingOfTypeArgument)) && reflect.TypeOf(actual).String() != string(expected.(AnythingOfTypeArgument)) {
+				// not match
+				differences++
+				output = fmt.Sprintf("%s\t%d: \u274C  type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actual)
+			}
+
+		} else {
+
+			// normal checking
+
+			if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
+				// match
+				output = fmt.Sprintf("%s\t%d: \u2705  %s == %s\n", output, i, actual, expected)
+			} else {
+				// not match
+				differences++
+				output = fmt.Sprintf("%s\t%d: \u274C  %s != %s\n", output, i, actual, expected)
+			}
+		}
+
+	}
+
+	if differences == 0 {
+		return "No differences.", differences
+	}
+
+	return output, differences
+
+}
+
+// Assert compares the arguments with the specified objects and fails if
+// they do not exactly match.
+func (args Arguments) Assert(t TestingT, objects ...interface{}) bool {
+
+	// get the differences
+	diff, diffCount := args.Diff(objects)
+
+	if diffCount == 0 {
+		return true
+	}
+
+	// there are differences... report them...
+	t.Logf(diff)
+	t.Errorf("%sArguments do not match.", assert.CallerInfo())
+
+	return false
+
+}
+
+// String gets the argument at the specified index. Panics if there is no argument, or
+// if the argument is of the wrong type.
+//
+// If no index is provided, String() returns a complete string representation
+// of the arguments.
+func (args Arguments) String(indexOrNil ...int) string {
+
+	if len(indexOrNil) == 0 {
+		// normal String() method - return a string representation of the args
+		var argsStr []string
+		for _, arg := range args {
+			argsStr = append(argsStr, fmt.Sprintf("%s", reflect.TypeOf(arg)))
+		}
+		return strings.Join(argsStr, ",")
+	} else if len(indexOrNil) == 1 {
+		// Index has been specified - get the argument at that index
+		var index int = indexOrNil[0]
+		var s string
+		var ok bool
+		if s, ok = args.Get(index).(string); !ok {
+			panic(fmt.Sprintf("assert: arguments: String(%d) failed because object wasn't correct type: %s", index, args.Get(index)))
+		}
+		return s
+	}
+
+	panic(fmt.Sprintf("assert: arguments: Wrong number of arguments passed to String.  Must be 0 or 1, not %d", len(indexOrNil)))
+
+}
+
+// Int gets the argument at the specified index. Panics if there is no argument, or
+// if the argument is of the wrong type.
+func (args Arguments) Int(index int) int {
+	var s int
+	var ok bool
+	if s, ok = args.Get(index).(int); !ok {
+		panic(fmt.Sprintf("assert: arguments: Int(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
+	}
+	return s
+}
+
+// Error gets the argument at the specified index. Panics if there is no argument, or
+// if the argument is of the wrong type.
+func (args Arguments) Error(index int) error {
+	obj := args.Get(index)
+	var s error
+	var ok bool
+	if obj == nil {
+		return nil
+	}
+	if s, ok = obj.(error); !ok {
+		panic(fmt.Sprintf("assert: arguments: Error(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
+	}
+	return s
+}
+
+// Bool gets the argument at the specified index. Panics if there is no argument, or
+// if the argument is of the wrong type.
+func (args Arguments) Bool(index int) bool {
+	var s bool
+	var ok bool
+	if s, ok = args.Get(index).(bool); !ok {
+		panic(fmt.Sprintf("assert: arguments: Bool(%d) failed because object wasn't correct type: %v", index, args.Get(index)))
+	}
+	return s
+}