mirror of https://github.com/docker/docs.git
Merge pull request #1601 from vieux/docker_discovery
use docker/docker/pkg/discovery and update godeps
This commit is contained in:
commit
254e095f77
|
@ -14,57 +14,62 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/coreos/etcd/client",
|
"ImportPath": "github.com/coreos/etcd/client",
|
||||||
"Comment": "v2.3.0-alpha.0-390-gc70d533",
|
"Comment": "v2.3.0-alpha.0-430-g374b14e",
|
||||||
"Rev": "c70d53377134659390f79c00b47e4f2bdd1d01d6"
|
"Rev": "374b14e47189c249c069c9b3376cf5c36f286fa6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/coreos/etcd/pkg/pathutil",
|
"ImportPath": "github.com/coreos/etcd/pkg/pathutil",
|
||||||
"Comment": "v2.3.0-alpha.0-390-gc70d533",
|
"Comment": "v2.3.0-alpha.0-430-g374b14e",
|
||||||
"Rev": "c70d53377134659390f79c00b47e4f2bdd1d01d6"
|
"Rev": "374b14e47189c249c069c9b3376cf5c36f286fa6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/coreos/etcd/pkg/types",
|
"ImportPath": "github.com/coreos/etcd/pkg/types",
|
||||||
"Comment": "v2.3.0-alpha.0-390-gc70d533",
|
"Comment": "v2.3.0-alpha.0-430-g374b14e",
|
||||||
"Rev": "c70d53377134659390f79c00b47e4f2bdd1d01d6"
|
"Rev": "374b14e47189c249c069c9b3376cf5c36f286fa6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||||
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/api/types/filters",
|
"ImportPath": "github.com/docker/docker/pkg/discovery",
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
"Comment": "v1.4.1-9107-gf11b6a2",
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
"Rev": "f11b6a2ab313a03d051dd6f69d264d0482df72d6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/ioutils",
|
"ImportPath": "github.com/docker/docker/pkg/ioutils",
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
"Comment": "v1.4.1-9107-gf11b6a2",
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
"Rev": "f11b6a2ab313a03d051dd6f69d264d0482df72d6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/longpath",
|
"ImportPath": "github.com/docker/docker/pkg/longpath",
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
"Comment": "v1.4.1-9107-gf11b6a2",
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
"Rev": "f11b6a2ab313a03d051dd6f69d264d0482df72d6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/random",
|
"ImportPath": "github.com/docker/docker/pkg/random",
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
"Comment": "v1.4.1-9107-gf11b6a2",
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
"Rev": "f11b6a2ab313a03d051dd6f69d264d0482df72d6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/stringid",
|
"ImportPath": "github.com/docker/docker/pkg/stringid",
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
"Comment": "v1.4.1-9107-gf11b6a2",
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
"Rev": "f11b6a2ab313a03d051dd6f69d264d0482df72d6"
|
||||||
},
|
|
||||||
{
|
|
||||||
"ImportPath": "github.com/docker/docker/pkg/tlsconfig",
|
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/docker/pkg/version",
|
"ImportPath": "github.com/docker/docker/pkg/version",
|
||||||
"Comment": "v1.4.1-8996-g7fab931",
|
"Comment": "v1.4.1-9107-gf11b6a2",
|
||||||
"Rev": "7fab93175d605f98cddf811819d9ab081bb4f90e"
|
"Rev": "f11b6a2ab313a03d051dd6f69d264d0482df72d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/engine-api/types/filters",
|
||||||
|
"Comment": "v0.2.0",
|
||||||
|
"Rev": "259fce04b5a17bc7a202090d52ca873762fa8b25"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/docker/go-connections/tlsconfig",
|
||||||
|
"Comment": "v0.1.2",
|
||||||
|
"Rev": "4e42727957c146776e5de9cec8c39e4059ed9f20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/docker/go-units",
|
"ImportPath": "github.com/docker/go-units",
|
||||||
|
@ -95,8 +100,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/hashicorp/consul/api",
|
"ImportPath": "github.com/hashicorp/consul/api",
|
||||||
"Comment": "v0.6.0-121-g68969ce",
|
"Comment": "v0.6.1-11-g562bf11",
|
||||||
"Rev": "68969ce5f4499cbe3a4f946917be2e580f1b1936"
|
"Rev": "562bf11e9ff784824f9c5fec0ad3609805e13a3d"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/hashicorp/go-cleanhttp",
|
"ImportPath": "github.com/hashicorp/go-cleanhttp",
|
||||||
|
@ -148,7 +153,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/pmezard/go-difflib/difflib",
|
"ImportPath": "github.com/pmezard/go-difflib/difflib",
|
||||||
"Rev": "e8554b8641db39598be7f6342874b958f12ae1d4"
|
"Rev": "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/samalba/dockerclient",
|
"ImportPath": "github.com/samalba/dockerclient",
|
||||||
|
@ -168,13 +173,13 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/stretchr/testify/assert",
|
"ImportPath": "github.com/stretchr/testify/assert",
|
||||||
"Comment": "v1.0-91-g5b9da39",
|
"Comment": "v1.1.3",
|
||||||
"Rev": "5b9da39b66e8e994455c2525c4421c8cc00a7f93"
|
"Rev": "f390dcf405f7b83c997eac1b06768bb9f44dec18"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/stretchr/testify/mock",
|
"ImportPath": "github.com/stretchr/testify/mock",
|
||||||
"Comment": "v1.0-91-g5b9da39",
|
"Comment": "v1.1.3",
|
||||||
"Rev": "5b9da39b66e8e994455c2525c4421c8cc00a7f93"
|
"Rev": "f390dcf405f7b83c997eac1b06768bb9f44dec18"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/ugorji/go/codec",
|
"ImportPath": "github.com/ugorji/go/codec",
|
||||||
|
@ -182,7 +187,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/net/context",
|
"ImportPath": "golang.org/x/net/context",
|
||||||
"Rev": "1ade16a5450925b7496e1031938175d1f5d30d31"
|
"Rev": "4fd4a9fed55e5bdee4a89d6406c2eabe38b60300"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "golang.org/x/sys/unix",
|
"ImportPath": "golang.org/x/sys/unix",
|
||||||
|
|
41
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/README.md
generated
vendored
Normal file
41
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/README.md
generated
vendored
Normal file
|
@ -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 `--cluster-advertise`
|
||||||
|
flag.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker daemon -H=<node_ip:2376> --cluster-advertise=<node_ip:2376> --cluster-store etcd://<etcd_ip1>,<etcd_ip2>/<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 `--cluster-advertise`
|
||||||
|
flag.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker daemon -H=<node_ip:2376> --cluster-advertise=<node_ip:2376> --cluster-store 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 `--cluster-advertise`
|
||||||
|
flag.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ docker daemon -H=<node_ip:2376> --cluster-advertise=<node_ip:2376> --cluster-store zk://<zk_addr1>,<zk_addr2>/<path>
|
||||||
|
```
|
111
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/backends.go
generated
vendored
Normal file
111
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/backends.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
||||||
|
package discovery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"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]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAdvertise parses the --cluster-advertise daemon config which accepts
|
||||||
|
// <ip-address>:<port> or <interface-name>:<port>
|
||||||
|
func ParseAdvertise(store, advertise string) (string, error) {
|
||||||
|
var (
|
||||||
|
iface *net.Interface
|
||||||
|
addrs []net.Addr
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
addr, port, err := net.SplitHostPort(advertise)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid --cluster-advertise configuration: %s: %v", advertise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip := net.ParseIP(addr)
|
||||||
|
// If it is a valid ip-address, use it as is
|
||||||
|
if ip != nil {
|
||||||
|
return advertise, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If advertise is a valid interface name, get the valid ipv4 address and use it to advertise
|
||||||
|
ifaceName := addr
|
||||||
|
iface, err = net.InterfaceByName(ifaceName)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("invalid cluster advertise IP address or interface name (%s) : %v", advertise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err = iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to get advertise IP address from interface (%s) : %v", advertise, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addrs == nil || len(addrs) == 0 {
|
||||||
|
return "", fmt.Errorf("no available advertise IP address in interface (%s)", advertise)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = ""
|
||||||
|
for _, a := range addrs {
|
||||||
|
ip, _, err := net.ParseCIDR(a.String())
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error deriving advertise ip-address in interface (%s) : %v", advertise, err)
|
||||||
|
}
|
||||||
|
if ip.To4() == nil || ip.IsLoopback() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
addr = ip.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if addr == "" {
|
||||||
|
return "", fmt.Errorf("couldnt find a valid ip-address in interface %s", advertise)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = fmt.Sprintf("%s:%s", addr, port)
|
||||||
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, clusterOpts map[string]string) (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, clusterOpts)
|
||||||
|
return backend, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrNotSupported
|
||||||
|
}
|
35
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/discovery.go
generated
vendored
Normal file
35
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/discovery.go
generated
vendored
Normal file
|
@ -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, a ttl and optional settings.
|
||||||
|
Initialize(string, time.Duration, time.Duration, map[string]string) error
|
||||||
|
|
||||||
|
// Register to the discovery.
|
||||||
|
Register(string) error
|
||||||
|
}
|
97
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/entry.go
generated
vendored
Normal file
97
Godeps/_workspace/src/github.com/docker/docker/pkg/discovery/entry.go
generated
vendored
Normal file
|
@ -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
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/swarm/discovery"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Discovery is exported
|
// Discovery is exported
|
|
@ -7,17 +7,17 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/pkg/tlsconfig"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
|
"github.com/docker/go-connections/tlsconfig"
|
||||||
"github.com/docker/libkv"
|
"github.com/docker/libkv"
|
||||||
"github.com/docker/libkv/store"
|
"github.com/docker/libkv/store"
|
||||||
"github.com/docker/libkv/store/consul"
|
"github.com/docker/libkv/store/consul"
|
||||||
"github.com/docker/libkv/store/etcd"
|
"github.com/docker/libkv/store/etcd"
|
||||||
"github.com/docker/libkv/store/zookeeper"
|
"github.com/docker/libkv/store/zookeeper"
|
||||||
"github.com/docker/swarm/discovery"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultDiscoveryPath = "docker/swarm/nodes"
|
defaultDiscoveryPath = "docker/nodes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Discovery is exported
|
// Discovery is exported
|
||||||
|
@ -41,14 +41,14 @@ func Init() {
|
||||||
consul.Register()
|
consul.Register()
|
||||||
etcd.Register()
|
etcd.Register()
|
||||||
|
|
||||||
// Register to internal Swarm discovery service
|
// Register to internal discovery service
|
||||||
discovery.Register("zk", &Discovery{backend: store.ZK})
|
discovery.Register("zk", &Discovery{backend: store.ZK})
|
||||||
discovery.Register("consul", &Discovery{backend: store.CONSUL})
|
discovery.Register("consul", &Discovery{backend: store.CONSUL})
|
||||||
discovery.Register("etcd", &Discovery{backend: store.ETCD})
|
discovery.Register("etcd", &Discovery{backend: store.ETCD})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize is exported
|
// Initialize is exported
|
||||||
func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration, discoveryOpt map[string]string) error {
|
func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) error {
|
||||||
var (
|
var (
|
||||||
parts = strings.SplitN(uris, "/", 2)
|
parts = strings.SplitN(uris, "/", 2)
|
||||||
addrs = strings.Split(parts[0], ",")
|
addrs = strings.Split(parts[0], ",")
|
||||||
|
@ -65,19 +65,19 @@ func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Du
|
||||||
|
|
||||||
// Use a custom path if specified in discovery options
|
// Use a custom path if specified in discovery options
|
||||||
dpath := defaultDiscoveryPath
|
dpath := defaultDiscoveryPath
|
||||||
if discoveryOpt["kv.path"] != "" {
|
if clusterOpts["kv.path"] != "" {
|
||||||
dpath = discoveryOpt["kv.path"]
|
dpath = clusterOpts["kv.path"]
|
||||||
}
|
}
|
||||||
|
|
||||||
s.path = path.Join(s.prefix, dpath)
|
s.path = path.Join(s.prefix, dpath)
|
||||||
|
|
||||||
var config *store.Config
|
var config *store.Config
|
||||||
if discoveryOpt["kv.cacertfile"] != "" && discoveryOpt["kv.certfile"] != "" && discoveryOpt["kv.keyfile"] != "" {
|
if clusterOpts["kv.cacertfile"] != "" && clusterOpts["kv.certfile"] != "" && clusterOpts["kv.keyfile"] != "" {
|
||||||
log.Debug("Initializing discovery with TLS")
|
log.Info("Initializing discovery with TLS")
|
||||||
tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
|
tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
|
||||||
CAFile: discoveryOpt["kv.cacertfile"],
|
CAFile: clusterOpts["kv.cacertfile"],
|
||||||
CertFile: discoveryOpt["kv.certfile"],
|
CertFile: clusterOpts["kv.certfile"],
|
||||||
KeyFile: discoveryOpt["kv.keyfile"],
|
KeyFile: clusterOpts["kv.keyfile"],
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -85,15 +85,15 @@ func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Du
|
||||||
config = &store.Config{
|
config = &store.Config{
|
||||||
// Set ClientTLS to trigger https (bug in libkv/etcd)
|
// Set ClientTLS to trigger https (bug in libkv/etcd)
|
||||||
ClientTLS: &store.ClientTLSConfig{
|
ClientTLS: &store.ClientTLSConfig{
|
||||||
CACertFile: discoveryOpt["kv.cacertfile"],
|
CACertFile: clusterOpts["kv.cacertfile"],
|
||||||
CertFile: discoveryOpt["kv.certfile"],
|
CertFile: clusterOpts["kv.certfile"],
|
||||||
KeyFile: discoveryOpt["kv.keyfile"],
|
KeyFile: clusterOpts["kv.keyfile"],
|
||||||
},
|
},
|
||||||
// The actual TLS config that will be used
|
// The actual TLS config that will be used
|
||||||
TLS: tlsConfig,
|
TLS: tlsConfig,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Debug("Initializing discovery without TLS")
|
log.Info("Initializing discovery without TLS")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new store, will ignore options given
|
// Creates a new store, will ignore options given
|
||||||
|
@ -172,7 +172,6 @@ func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-c
|
||||||
time.Sleep(s.heartbeat)
|
time.Sleep(s.heartbeat)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return ch, errCh
|
return ch, errCh
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/swarm/discovery"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Discovery is exported
|
// Discovery is exported
|
191
Godeps/_workspace/src/github.com/docker/docker/vendor/src/github.com/docker/engine-api/LICENSE
generated
vendored
Normal file
191
Godeps/_workspace/src/github.com/docker/docker/vendor/src/github.com/docker/engine-api/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2015-2016 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
9
Godeps/_workspace/src/github.com/docker/docker/vendor/src/github.com/miekg/dns/COPYRIGHT
generated
vendored
Normal file
9
Godeps/_workspace/src/github.com/docker/docker/vendor/src/github.com/miekg/dns/COPYRIGHT
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Copyright 2009 The Go Authors. All rights reserved. Use of this source code
|
||||||
|
is governed by a BSD-style license that can be found in the LICENSE file.
|
||||||
|
Extensions of the original work are copyright (c) 2011 Miek Gieben
|
||||||
|
|
||||||
|
Copyright 2011 Miek Gieben. All rights reserved. Use of this source code is
|
||||||
|
governed by a BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
Copyright 2014 CloudFlare. All rights reserved. Use of this source code is
|
||||||
|
governed by a BSD-style license that can be found in the LICENSE file.
|
32
Godeps/_workspace/src/github.com/docker/docker/vendor/src/github.com/miekg/dns/LICENSE
generated
vendored
Normal file
32
Godeps/_workspace/src/github.com/docker/docker/vendor/src/github.com/miekg/dns/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
Extensions of the original work are copyright (c) 2011 Miek Gieben
|
||||||
|
|
||||||
|
As this is fork of the official Go code the same license applies:
|
||||||
|
|
||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2015-2016 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2015 Docker, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
|
@ -585,6 +585,7 @@ func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
||||||
if len(diff.ToDate) > 0 {
|
if len(diff.ToDate) > 0 {
|
||||||
toDate = "\t" + diff.ToDate
|
toDate = "\t" + diff.ToDate
|
||||||
}
|
}
|
||||||
|
if diff.FromFile != "" || diff.ToFile != "" {
|
||||||
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -594,6 +595,7 @@ func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
first, last := g[0], g[len(g)-1]
|
first, last := g[0], g[len(g)-1]
|
||||||
range1 := formatRangeUnified(first.I1, last.I2)
|
range1 := formatRangeUnified(first.I1, last.I2)
|
||||||
range2 := formatRangeUnified(first.J1, last.J2)
|
range2 := formatRangeUnified(first.J1, last.J2)
|
||||||
|
@ -710,9 +712,11 @@ func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
|
||||||
if len(diff.ToDate) > 0 {
|
if len(diff.ToDate) > 0 {
|
||||||
toDate = "\t" + diff.ToDate
|
toDate = "\t" + diff.ToDate
|
||||||
}
|
}
|
||||||
|
if diff.FromFile != "" || diff.ToFile != "" {
|
||||||
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||||
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
|
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
first, last := g[0], g[len(g)-1]
|
first, last := g[0], g[len(g)-1]
|
||||||
ws("***************" + diff.Eol)
|
ws("***************" + diff.Eol)
|
||||||
|
|
387
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
Normal file
387
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertion_forward.go
generated
vendored
Normal file
|
@ -0,0 +1,387 @@
|
||||||
|
/*
|
||||||
|
* CODE GENERATED AUTOMATICALLY WITH github.com/stretchr/testify/_codegen
|
||||||
|
* THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
|
*/
|
||||||
|
|
||||||
|
package assert
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
http "net/http"
|
||||||
|
url "net/url"
|
||||||
|
time "time"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// Condition uses a Comparison to assert a complex condition.
|
||||||
|
func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool {
|
||||||
|
return Condition(a.t, comp, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Contains asserts that the specified string, list(array, slice...) or map contains the
|
||||||
|
// specified substring or element.
|
||||||
|
//
|
||||||
|
// a.Contains("Hello World", "World", "But 'Hello World' does contain 'World'")
|
||||||
|
// a.Contains(["Hello", "World"], "World", "But ["Hello", "World"] does contain 'World'")
|
||||||
|
// a.Contains({"Hello": "World"}, "Hello", "But {'Hello': 'World'} does contain 'Hello'")
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return Contains(a.t, s, contains, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||||
|
// a slice or a channel with len == 0.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Equal asserts that two objects are equal.
|
||||||
|
//
|
||||||
|
// a.Equal(123, 123, "123 and 123 should be equal")
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return Equal(a.t, expected, actual, 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(t, err, "An error was expected") {
|
||||||
|
// assert.Equal(t, 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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EqualValues asserts that two objects are equal or convertable to the same types
|
||||||
|
// and equal.
|
||||||
|
//
|
||||||
|
// a.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 interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return EqualValues(a.t, expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Error asserts that a function returned an error (i.e. not `nil`).
|
||||||
|
//
|
||||||
|
// actualObj, err := SomeFunction()
|
||||||
|
// if a.Error(err, "An error was expected") {
|
||||||
|
// assert.Equal(t, err, expectedError)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
|
||||||
|
return Error(a.t, err, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Exactly asserts that two objects are equal is value and type.
|
||||||
|
//
|
||||||
|
// a.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 interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return Exactly(a.t, expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fail reports a failure through
|
||||||
|
func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool {
|
||||||
|
return Fail(a.t, failureMessage, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// FailNow fails test
|
||||||
|
func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool {
|
||||||
|
return FailNow(a.t, failureMessage, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// False asserts that the specified value is false.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HTTPBodyContains asserts that a specified handler returns a
|
||||||
|
// body that contains a string.
|
||||||
|
//
|
||||||
|
// a.HTTPBodyContains(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 string, 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.
|
||||||
|
//
|
||||||
|
// a.HTTPBodyNotContains(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 string, url string, values url.Values, str interface{}) bool {
|
||||||
|
return HTTPBodyNotContains(a.t, handler, method, url, values, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HTTPError asserts that a specified handler returns an error status code.
|
||||||
|
//
|
||||||
|
// a.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 string, url string, values url.Values) bool {
|
||||||
|
return HTTPError(a.t, handler, method, url, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HTTPRedirect asserts that a specified handler returns a redirect status code.
|
||||||
|
//
|
||||||
|
// a.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 string, url string, values url.Values) bool {
|
||||||
|
return HTTPRedirect(a.t, handler, method, url, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// HTTPSuccess asserts that a specified handler returns a success status code.
|
||||||
|
//
|
||||||
|
// a.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 string, url string, values url.Values) bool {
|
||||||
|
return HTTPSuccess(a.t, handler, method, url, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Implements asserts that an object is implemented by the specified interface.
|
||||||
|
//
|
||||||
|
// a.Implements((*MyInterface)(nil), new(MyObject), "MyObject")
|
||||||
|
func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return Implements(a.t, interfaceObject, object, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// InDelta asserts that the two numerals are within delta of each other.
|
||||||
|
//
|
||||||
|
// a.InDelta(math.Pi, (22 / 7.0), 0.01)
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||||
|
return InDelta(a.t, expected, actual, delta, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// InDeltaSlice is the same as InDelta, except it compares two slices.
|
||||||
|
func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||||
|
return InDeltaSlice(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 interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||||
|
return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
|
||||||
|
func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
||||||
|
return InEpsilonSlice(a.t, expected, actual, delta, 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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// JSONEq asserts that two JSON strings are equivalent.
|
||||||
|
//
|
||||||
|
// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
||||||
|
return JSONEq(a.t, expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Len asserts that the specified object has specific length.
|
||||||
|
// Len also fails if the object has a type that len() not accept.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Nil asserts that the specified object is nil.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NoError asserts that a function returned no error (i.e. `nil`).
|
||||||
|
//
|
||||||
|
// actualObj, err := SomeFunction()
|
||||||
|
// if a.NoError(err) {
|
||||||
|
// assert.Equal(t, actualObj, expectedObj)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool {
|
||||||
|
return NoError(a.t, err, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the
|
||||||
|
// specified substring or element.
|
||||||
|
//
|
||||||
|
// a.NotContains("Hello World", "Earth", "But 'Hello World' does NOT contain 'Earth'")
|
||||||
|
// a.NotContains(["Hello", "World"], "Earth", "But ['Hello', 'World'] does NOT contain 'Earth'")
|
||||||
|
// a.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 interface{}, contains interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return NotContains(a.t, s, contains, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 a.NotEmpty(obj) {
|
||||||
|
// assert.Equal(t, "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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NotEqual asserts that the specified values are NOT equal.
|
||||||
|
//
|
||||||
|
// a.NotEqual(obj1, obj2, "two objects shouldn't be equal")
|
||||||
|
//
|
||||||
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
|
func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||||
|
return NotEqual(a.t, expected, actual, msgAndArgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NotNil asserts that the specified object is not nil.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NotRegexp asserts that a specified regexp does not match a string.
|
||||||
|
//
|
||||||
|
// a.NotRegexp(regexp.MustCompile("starts"), "it's starting")
|
||||||
|
// a.NotRegexp("^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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Panics asserts that the code inside the specified PanicTestFunc panics.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Regexp asserts that a specified regexp matches a string.
|
||||||
|
//
|
||||||
|
// a.Regexp(regexp.MustCompile("start"), "it's starting")
|
||||||
|
// a.Regexp("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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// True asserts that the specified value is true.
|
||||||
|
//
|
||||||
|
// a.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...)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// WithinDuration asserts that the two times are within duration delta of each other.
|
||||||
|
//
|
||||||
|
// a.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 time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool {
|
||||||
|
return WithinDuration(a.t, expected, actual, delta, 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...)
|
||||||
|
}
|
4
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertion_forward.go.tmpl
generated
vendored
Normal file
4
Godeps/_workspace/src/github.com/stretchr/testify/assert/assertion_forward.go.tmpl
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{{.CommentWithoutT "a"}}
|
||||||
|
func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool {
|
||||||
|
return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}})
|
||||||
|
}
|
|
@ -181,6 +181,28 @@ func indentMessageLines(message string, tabs int) string {
|
||||||
return outBuf.String()
|
return outBuf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type failNower interface {
|
||||||
|
FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailNow fails test
|
||||||
|
func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||||
|
Fail(t, failureMessage, msgAndArgs...)
|
||||||
|
|
||||||
|
// We cannot extend TestingT with FailNow() and
|
||||||
|
// maintain backwards compatibility, so we fallback
|
||||||
|
// to panicking when FailNow is not available in
|
||||||
|
// TestingT.
|
||||||
|
// See issue #263
|
||||||
|
|
||||||
|
if t, ok := t.(failNower); ok {
|
||||||
|
t.FailNow()
|
||||||
|
} else {
|
||||||
|
panic("test failed and t is missing `FailNow()`")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Fail reports a failure through
|
// Fail reports a failure through
|
||||||
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||||
|
|
||||||
|
@ -363,6 +385,11 @@ func isEmpty(object interface{}) bool {
|
||||||
{
|
{
|
||||||
return (objValue.Len() == 0)
|
return (objValue.Len() == 0)
|
||||||
}
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
switch object.(type) {
|
||||||
|
case time.Time:
|
||||||
|
return object.(time.Time).IsZero()
|
||||||
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
{
|
{
|
||||||
if objValue.IsNil() {
|
if objValue.IsNil() {
|
||||||
|
@ -739,42 +766,40 @@ func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAn
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// min(|expected|, |actual|) * epsilon
|
func calcRelativeError(expected, actual interface{}) (float64, error) {
|
||||||
func calcEpsilonDelta(expected, actual interface{}, epsilon float64) float64 {
|
|
||||||
af, aok := toFloat(expected)
|
af, aok := toFloat(expected)
|
||||||
|
if !aok {
|
||||||
|
return 0, fmt.Errorf("expected value %q cannot be converted to float", expected)
|
||||||
|
}
|
||||||
|
if af == 0 {
|
||||||
|
return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error")
|
||||||
|
}
|
||||||
bf, bok := toFloat(actual)
|
bf, bok := toFloat(actual)
|
||||||
|
if !bok {
|
||||||
if !aok || !bok {
|
return 0, fmt.Errorf("expected value %q cannot be converted to float", actual)
|
||||||
// invalid input
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if af < 0 {
|
return math.Abs(af-bf) / math.Abs(af), nil
|
||||||
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
|
// InEpsilon asserts that expected and actual have a relative error less than epsilon
|
||||||
//
|
//
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
// Returns whether the assertion was successful (true) or not (false).
|
||||||
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||||
delta := calcEpsilonDelta(expected, actual, epsilon)
|
actualEpsilon, err := calcRelativeError(expected, actual)
|
||||||
|
if err != nil {
|
||||||
return InDelta(t, expected, actual, delta, msgAndArgs...)
|
return Fail(t, err.Error(), msgAndArgs...)
|
||||||
|
}
|
||||||
|
if actualEpsilon > epsilon {
|
||||||
|
return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+
|
||||||
|
" < %#v (actual)", actualEpsilon, epsilon), msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InEpsilonSlice is the same as InEpsilon, except it compares two slices.
|
return true
|
||||||
func InEpsilonSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool {
|
}
|
||||||
|
|
||||||
|
// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices.
|
||||||
|
func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool {
|
||||||
if expected == nil || actual == nil ||
|
if expected == nil || actual == nil ||
|
||||||
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
reflect.TypeOf(actual).Kind() != reflect.Slice ||
|
||||||
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
reflect.TypeOf(expected).Kind() != reflect.Slice {
|
||||||
|
@ -785,7 +810,7 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, delta float64, msg
|
||||||
expectedSlice := reflect.ValueOf(expected)
|
expectedSlice := reflect.ValueOf(expected)
|
||||||
|
|
||||||
for i := 0; i < actualSlice.Len(); i++ {
|
for i := 0; i < actualSlice.Len(); i++ {
|
||||||
result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta)
|
result := InEpsilon(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), epsilon)
|
||||||
if !result {
|
if !result {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
270
Godeps/_workspace/src/github.com/stretchr/testify/assert/forward_assertions.go
generated
vendored
270
Godeps/_workspace/src/github.com/stretchr/testify/assert/forward_assertions.go
generated
vendored
|
@ -1,7 +1,5 @@
|
||||||
package assert
|
package assert
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Assertions provides assertion methods around the
|
// Assertions provides assertion methods around the
|
||||||
// TestingT interface.
|
// TestingT interface.
|
||||||
type Assertions struct {
|
type Assertions struct {
|
||||||
|
@ -15,270 +13,4 @@ func New(t TestingT) *Assertions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fail reports a failure through
|
//go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl
|
||||||
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 false.
|
|
||||||
//
|
|
||||||
// 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...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// JSONEq asserts that two JSON strings are equivalent.
|
|
||||||
//
|
|
||||||
// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)
|
|
||||||
//
|
|
||||||
// Returns whether the assertion was successful (true) or not (false).
|
|
||||||
func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool {
|
|
||||||
return JSONEq(a.t, expected, actual, msgAndArgs...)
|
|
||||||
}
|
|
||||||
|
|
|
@ -104,54 +104,3 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url strin
|
||||||
|
|
||||||
return !contains
|
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Provides a system by which it is possible to mock your objects and verify calls are happening as expected.
|
// Package mock provides a system by which it is possible to mock your objects
|
||||||
|
// and verify calls are happening as expected.
|
||||||
//
|
//
|
||||||
// Example Usage
|
// Example Usage
|
||||||
//
|
//
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
type TestingT interface {
|
type TestingT interface {
|
||||||
Logf(format string, args ...interface{})
|
Logf(format string, args ...interface{})
|
||||||
Errorf(format string, args ...interface{})
|
Errorf(format string, args ...interface{})
|
||||||
|
FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -64,64 +65,67 @@ func newCall(parent *Mock, methodName string, methodArguments ...interface{}) *C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Call) lock() {
|
func (c *Call) lock() {
|
||||||
self.Parent.mutex.Lock()
|
c.Parent.mutex.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Call) unlock() {
|
func (c *Call) unlock() {
|
||||||
self.Parent.mutex.Unlock()
|
c.Parent.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Call) Return(returnArguments ...interface{}) *Call {
|
// Return specifies the return arguments for the expectation.
|
||||||
self.lock()
|
//
|
||||||
defer self.unlock()
|
// Mock.On("DoSomething").Return(errors.New("failed"))
|
||||||
|
func (c *Call) Return(returnArguments ...interface{}) *Call {
|
||||||
|
c.lock()
|
||||||
|
defer c.unlock()
|
||||||
|
|
||||||
self.ReturnArguments = returnArguments
|
c.ReturnArguments = returnArguments
|
||||||
|
|
||||||
return self
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once indicates that that the mock should only return the value once.
|
// Once indicates that that the mock should only return the value once.
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once()
|
||||||
func (self *Call) Once() *Call {
|
func (c *Call) Once() *Call {
|
||||||
return self.Times(1)
|
return c.Times(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Twice indicates that that the mock should only return the value twice.
|
// Twice indicates that that the mock should only return the value twice.
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice()
|
||||||
func (self *Call) Twice() *Call {
|
func (c *Call) Twice() *Call {
|
||||||
return self.Times(2)
|
return c.Times(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Times indicates that that the mock should only return the indicated number
|
// Times indicates that that the mock should only return the indicated number
|
||||||
// of times.
|
// of times.
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5)
|
||||||
func (self *Call) Times(i int) *Call {
|
func (c *Call) Times(i int) *Call {
|
||||||
self.lock()
|
c.lock()
|
||||||
defer self.unlock()
|
defer c.unlock()
|
||||||
self.Repeatability = i
|
c.Repeatability = i
|
||||||
return self
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitUntil sets the channel that will block the mock's return until its closed
|
// WaitUntil sets the channel that will block the mock's return until its closed
|
||||||
// or a message is received.
|
// or a message is received.
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
|
// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second))
|
||||||
func (self *Call) WaitUntil(w <-chan time.Time) *Call {
|
func (c *Call) WaitUntil(w <-chan time.Time) *Call {
|
||||||
self.lock()
|
c.lock()
|
||||||
defer self.unlock()
|
defer c.unlock()
|
||||||
self.WaitFor = w
|
c.WaitFor = w
|
||||||
return self
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// After sets how long to block until the call returns
|
// After sets how long to block until the call returns
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
|
// Mock.On("MyMethod", arg1, arg2).After(time.Second)
|
||||||
func (self *Call) After(d time.Duration) *Call {
|
func (c *Call) After(d time.Duration) *Call {
|
||||||
return self.WaitUntil(time.After(d))
|
return c.WaitUntil(time.After(d))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run sets a handler to be called before returning. It can be used when
|
// Run sets a handler to be called before returning. It can be used when
|
||||||
|
@ -132,11 +136,11 @@ func (self *Call) After(d time.Duration) *Call {
|
||||||
// arg := args.Get(0).(*map[string]interface{})
|
// arg := args.Get(0).(*map[string]interface{})
|
||||||
// arg["foo"] = "bar"
|
// arg["foo"] = "bar"
|
||||||
// })
|
// })
|
||||||
func (self *Call) Run(fn func(Arguments)) *Call {
|
func (c *Call) Run(fn func(Arguments)) *Call {
|
||||||
self.lock()
|
c.lock()
|
||||||
defer self.unlock()
|
defer c.unlock()
|
||||||
self.RunFn = fn
|
c.RunFn = fn
|
||||||
return self
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
// On chains a new expectation description onto the mocked interface. This
|
// On chains a new expectation description onto the mocked interface. This
|
||||||
|
@ -145,8 +149,8 @@ func (self *Call) Run(fn func(Arguments)) *Call {
|
||||||
// Mock.
|
// Mock.
|
||||||
// On("MyMethod", 1).Return(nil).
|
// On("MyMethod", 1).Return(nil).
|
||||||
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error"))
|
||||||
func (self *Call) On(methodName string, arguments ...interface{}) *Call {
|
func (c *Call) On(methodName string, arguments ...interface{}) *Call {
|
||||||
return self.Parent.On(methodName, arguments...)
|
return c.Parent.On(methodName, arguments...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock is the workhorse used to track activity on another object.
|
// Mock is the workhorse used to track activity on another object.
|
||||||
|
@ -186,17 +190,17 @@ func (m *Mock) TestData() objx.Map {
|
||||||
// being called.
|
// being called.
|
||||||
//
|
//
|
||||||
// Mock.On("MyMethod", arg1, arg2)
|
// Mock.On("MyMethod", arg1, arg2)
|
||||||
func (self *Mock) On(methodName string, arguments ...interface{}) *Call {
|
func (m *Mock) On(methodName string, arguments ...interface{}) *Call {
|
||||||
for _, arg := range arguments {
|
for _, arg := range arguments {
|
||||||
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
|
if v := reflect.ValueOf(arg); v.Kind() == reflect.Func {
|
||||||
panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
|
panic(fmt.Sprintf("cannot use Func in expectations. Use mock.AnythingOfType(\"%T\")", arg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.mutex.Lock()
|
m.mutex.Lock()
|
||||||
defer self.mutex.Unlock()
|
defer m.mutex.Unlock()
|
||||||
c := newCall(self, methodName, arguments...)
|
c := newCall(m, methodName, arguments...)
|
||||||
self.ExpectedCalls = append(self.ExpectedCalls, c)
|
m.ExpectedCalls = append(m.ExpectedCalls, c)
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +226,7 @@ func (m *Mock) findExpectedCall(method string, arguments ...interface{}) (int, *
|
||||||
|
|
||||||
func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *Call) {
|
func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *Call) {
|
||||||
diffCount := 0
|
diffCount := 0
|
||||||
var closestCall *Call = nil
|
var closestCall *Call
|
||||||
|
|
||||||
for _, call := range m.expectedCalls() {
|
for _, call := range m.expectedCalls() {
|
||||||
if call.Method == method {
|
if call.Method == method {
|
||||||
|
@ -245,7 +249,7 @@ func (m *Mock) findClosestCall(method string, arguments ...interface{}) (bool, *
|
||||||
|
|
||||||
func callString(method string, arguments Arguments, includeArgumentValues bool) string {
|
func callString(method string, arguments Arguments, includeArgumentValues bool) string {
|
||||||
|
|
||||||
var argValsString string = ""
|
var argValsString string
|
||||||
if includeArgumentValues {
|
if includeArgumentValues {
|
||||||
var argVals []string
|
var argVals []string
|
||||||
for argIndex, arg := range arguments {
|
for argIndex, arg := range arguments {
|
||||||
|
@ -303,7 +307,7 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
|
||||||
call.Repeatability = -1
|
call.Repeatability = -1
|
||||||
|
|
||||||
case call.Repeatability > 1:
|
case call.Repeatability > 1:
|
||||||
call.Repeatability -= 1
|
call.Repeatability--
|
||||||
}
|
}
|
||||||
m.mutex.Unlock()
|
m.mutex.Unlock()
|
||||||
}
|
}
|
||||||
|
@ -334,7 +338,7 @@ func (m *Mock) Called(arguments ...interface{}) Arguments {
|
||||||
//
|
//
|
||||||
// Calls may have occurred in any order.
|
// Calls may have occurred in any order.
|
||||||
func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
||||||
var success bool = true
|
var success = true
|
||||||
for _, obj := range testObjects {
|
for _, obj := range testObjects {
|
||||||
mockObj := obj.(Mock)
|
mockObj := obj.(Mock)
|
||||||
success = success && mockObj.AssertExpectations(t)
|
success = success && mockObj.AssertExpectations(t)
|
||||||
|
@ -345,8 +349,8 @@ func AssertExpectationsForObjects(t TestingT, testObjects ...interface{}) bool {
|
||||||
// AssertExpectations asserts that everything specified with On and Return was
|
// AssertExpectations asserts that everything specified with On and Return was
|
||||||
// in fact called as expected. Calls may have occurred in any order.
|
// in fact called as expected. Calls may have occurred in any order.
|
||||||
func (m *Mock) AssertExpectations(t TestingT) bool {
|
func (m *Mock) AssertExpectations(t TestingT) bool {
|
||||||
var somethingMissing bool = false
|
var somethingMissing bool
|
||||||
var failedExpectations int = 0
|
var failedExpectations int
|
||||||
|
|
||||||
// iterate through each expectation
|
// iterate through each expectation
|
||||||
expectedCalls := m.expectedCalls()
|
expectedCalls := m.expectedCalls()
|
||||||
|
@ -376,7 +380,7 @@ func (m *Mock) AssertExpectations(t TestingT) bool {
|
||||||
|
|
||||||
// AssertNumberOfCalls asserts that the method was called expectedCalls times.
|
// AssertNumberOfCalls asserts that the method was called expectedCalls times.
|
||||||
func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool {
|
func (m *Mock) AssertNumberOfCalls(t TestingT, methodName string, expectedCalls int) bool {
|
||||||
var actualCalls int = 0
|
var actualCalls int
|
||||||
for _, call := range m.calls() {
|
for _, call := range m.calls() {
|
||||||
if call.Method == methodName {
|
if call.Method == methodName {
|
||||||
actualCalls++
|
actualCalls++
|
||||||
|
@ -440,8 +444,8 @@ func (m *Mock) calls() []Call {
|
||||||
type Arguments []interface{}
|
type Arguments []interface{}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// The "any" argument. Used in Diff and Assert when
|
// Anything is used in Diff and Assert when the argument being tested
|
||||||
// the argument being tested shouldn't be taken into consideration.
|
// shouldn't be taken into consideration.
|
||||||
Anything string = "mock.Anything"
|
Anything string = "mock.Anything"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -471,9 +475,8 @@ func (f argumentMatcher) Matches(argument interface{}) bool {
|
||||||
if reflect.TypeOf(argument).AssignableTo(expectType) {
|
if reflect.TypeOf(argument).AssignableTo(expectType) {
|
||||||
result := f.fn.Call([]reflect.Value{reflect.ValueOf(argument)})
|
result := f.fn.Call([]reflect.Value{reflect.ValueOf(argument)})
|
||||||
return result[0].Bool()
|
return result[0].Bool()
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f argumentMatcher) String() string {
|
func (f argumentMatcher) String() string {
|
||||||
|
@ -531,10 +534,10 @@ func (args Arguments) Is(objects ...interface{}) bool {
|
||||||
// Returns the diff string and number of differences found.
|
// Returns the diff string and number of differences found.
|
||||||
func (args Arguments) Diff(objects []interface{}) (string, int) {
|
func (args Arguments) Diff(objects []interface{}) (string, int) {
|
||||||
|
|
||||||
var output string = "\n"
|
var output = "\n"
|
||||||
var differences int
|
var differences int
|
||||||
|
|
||||||
var maxArgCount int = len(args)
|
var maxArgCount = len(args)
|
||||||
if len(objects) > maxArgCount {
|
if len(objects) > maxArgCount {
|
||||||
maxArgCount = len(objects)
|
maxArgCount = len(objects)
|
||||||
}
|
}
|
||||||
|
@ -629,7 +632,7 @@ func (args Arguments) String(indexOrNil ...int) string {
|
||||||
return strings.Join(argsStr, ",")
|
return strings.Join(argsStr, ",")
|
||||||
} else if len(indexOrNil) == 1 {
|
} else if len(indexOrNil) == 1 {
|
||||||
// Index has been specified - get the argument at that index
|
// Index has been specified - get the argument at that index
|
||||||
var index int = indexOrNil[0]
|
var index = indexOrNil[0]
|
||||||
var s string
|
var s string
|
||||||
var ok bool
|
var ok bool
|
||||||
if s, ok = args.Get(index).(string); !ok {
|
if s, ok = args.Get(index).(string); !ok {
|
||||||
|
|
13
Godeps/_workspace/src/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
13
Godeps/_workspace/src/github.com/stretchr/testify/vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
Copyright (c) 2012-2013 Dave Collins <dave@davec.name>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
27
Godeps/_workspace/src/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
Normal file
27
Godeps/_workspace/src/github.com/stretchr/testify/vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
Copyright (c) 2013, Patrick Mezard
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
The names of its contributors may not be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
23
Godeps/_workspace/src/github.com/stretchr/testify/vendor/github.com/stretchr/objx/LICENSE.md
generated
vendored
Normal file
23
Godeps/_workspace/src/github.com/stretchr/testify/vendor/github.com/stretchr/objx/LICENSE.md
generated
vendored
Normal file
|
@ -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.
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
dockerfilters "github.com/docker/docker/api/types/filters"
|
dockerfilters "github.com/docker/engine-api/types/filters"
|
||||||
"github.com/docker/swarm/cluster"
|
"github.com/docker/swarm/cluster"
|
||||||
"github.com/docker/swarm/version"
|
"github.com/docker/swarm/version"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/swarm/discovery"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkAddrFormat(addr string) bool {
|
func checkAddrFormat(addr string) bool {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
"github.com/docker/swarm/discovery"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
func list(c *cli.Context) {
|
func list(c *cli.Context) {
|
||||||
|
|
|
@ -11,12 +11,12 @@ import (
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/docker/docker/pkg/discovery"
|
||||||
|
kvdiscovery "github.com/docker/docker/pkg/discovery/kv"
|
||||||
"github.com/docker/swarm/api"
|
"github.com/docker/swarm/api"
|
||||||
"github.com/docker/swarm/cluster"
|
"github.com/docker/swarm/cluster"
|
||||||
"github.com/docker/swarm/cluster/mesos"
|
"github.com/docker/swarm/cluster/mesos"
|
||||||
"github.com/docker/swarm/cluster/swarm"
|
"github.com/docker/swarm/cluster/swarm"
|
||||||
"github.com/docker/swarm/discovery"
|
|
||||||
kvdiscovery "github.com/docker/swarm/discovery/kv"
|
|
||||||
"github.com/docker/swarm/leadership"
|
"github.com/docker/swarm/leadership"
|
||||||
"github.com/docker/swarm/scheduler"
|
"github.com/docker/swarm/scheduler"
|
||||||
"github.com/docker/swarm/scheduler/filter"
|
"github.com/docker/swarm/scheduler/filter"
|
||||||
|
@ -98,7 +98,7 @@ func loadTLSConfig(ca, cert, key string, verify bool) (*tls.Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the discovery service.
|
// Initialize the discovery service.
|
||||||
func createDiscovery(uri string, c *cli.Context, discoveryOpt []string) discovery.Discovery {
|
func createDiscovery(uri string, c *cli.Context, discoveryOpt []string) discovery.Backend {
|
||||||
hb, err := time.ParseDuration(c.String("heartbeat"))
|
hb, err := time.ParseDuration(c.String("heartbeat"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("invalid --heartbeat: %v", err)
|
log.Fatalf("invalid --heartbeat: %v", err)
|
||||||
|
@ -126,10 +126,13 @@ func getDiscoveryOpt(c *cli.Context) map[string]string {
|
||||||
kvpair := strings.SplitN(option, "=", 2)
|
kvpair := strings.SplitN(option, "=", 2)
|
||||||
options[kvpair[0]] = kvpair[1]
|
options[kvpair[0]] = kvpair[1]
|
||||||
}
|
}
|
||||||
|
if _, ok := options["kv.path"]; !ok {
|
||||||
|
options["kv.path"] = "docker/swarm/nodes"
|
||||||
|
}
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupReplication(c *cli.Context, cluster cluster.Cluster, server *api.Server, discovery discovery.Discovery, addr string, leaderTTL time.Duration, tlsConfig *tls.Config) {
|
func setupReplication(c *cli.Context, cluster cluster.Cluster, server *api.Server, discovery discovery.Backend, addr string, leaderTTL time.Duration, tlsConfig *tls.Config) {
|
||||||
kvDiscovery, ok := discovery.(*kvdiscovery.Discovery)
|
kvDiscovery, ok := discovery.(*kvdiscovery.Discovery)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Fatal("Leader election is only supported with consul, etcd and zookeeper discovery.")
|
log.Fatal("Leader election is only supported with consul, etcd and zookeeper discovery.")
|
||||||
|
|
|
@ -3,7 +3,7 @@ package cluster
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
dockerfilters "github.com/docker/docker/api/types/filters"
|
dockerfilters "github.com/docker/engine-api/types/filters"
|
||||||
"github.com/samalba/dockerclient"
|
"github.com/samalba/dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package cluster
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
dockerfilters "github.com/docker/docker/api/types/filters"
|
dockerfilters "github.com/docker/engine-api/types/filters"
|
||||||
"github.com/samalba/dockerclient"
|
"github.com/samalba/dockerclient"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,10 +12,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/pkg/discovery"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/go-units"
|
"github.com/docker/go-units"
|
||||||
"github.com/docker/swarm/cluster"
|
"github.com/docker/swarm/cluster"
|
||||||
"github.com/docker/swarm/discovery"
|
|
||||||
"github.com/docker/swarm/scheduler"
|
"github.com/docker/swarm/scheduler"
|
||||||
"github.com/docker/swarm/scheduler/node"
|
"github.com/docker/swarm/scheduler/node"
|
||||||
"github.com/samalba/dockerclient"
|
"github.com/samalba/dockerclient"
|
||||||
|
@ -54,7 +54,7 @@ type Cluster struct {
|
||||||
engines map[string]*cluster.Engine
|
engines map[string]*cluster.Engine
|
||||||
pendingEngines map[string]*cluster.Engine
|
pendingEngines map[string]*cluster.Engine
|
||||||
scheduler *scheduler.Scheduler
|
scheduler *scheduler.Scheduler
|
||||||
discovery discovery.Discovery
|
discovery discovery.Backend
|
||||||
pendingContainers map[string]*pendingContainer
|
pendingContainers map[string]*pendingContainer
|
||||||
|
|
||||||
overcommitRatio float64
|
overcommitRatio float64
|
||||||
|
@ -63,7 +63,7 @@ type Cluster struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCluster is exported
|
// NewCluster is exported
|
||||||
func NewCluster(scheduler *scheduler.Scheduler, TLSConfig *tls.Config, discovery discovery.Discovery, options cluster.DriverOpts, engineOptions *cluster.EngineOpts) (cluster.Cluster, error) {
|
func NewCluster(scheduler *scheduler.Scheduler, TLSConfig *tls.Config, discovery discovery.Backend, options cluster.DriverOpts, engineOptions *cluster.EngineOpts) (cluster.Cluster, error) {
|
||||||
log.WithFields(log.Fields{"name": "swarm"}).Debug("Initializing cluster")
|
log.WithFields(log.Fields{"name": "swarm"}).Debug("Initializing cluster")
|
||||||
|
|
||||||
cluster := &Cluster{
|
cluster := &Cluster{
|
||||||
|
|
|
@ -1,166 +0,0 @@
|
||||||
package discovery
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An Entry represents a swarm host.
|
|
||||||
type Entry struct {
|
|
||||||
Host string
|
|
||||||
Port string
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns the string form of an entry.
|
|
||||||
func (e *Entry) String() string {
|
|
||||||
return fmt.Sprintf("%s:%s", e.Host, e.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Discovery interface is implemented by Discovery backends which
|
|
||||||
// manage swarm host entries.
|
|
||||||
type Discovery interface {
|
|
||||||
// Initialize the discovery with URIs, a heartbeat, a ttl and optional settings.
|
|
||||||
Initialize(string, time.Duration, time.Duration, map[string]string) error
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// Register to the discovery
|
|
||||||
Register(string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
discoveries map[string]Discovery
|
|
||||||
// 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")
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
discoveries = make(map[string]Discovery)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 Discovery) error {
|
|
||||||
if _, exists := discoveries[scheme]; exists {
|
|
||||||
return fmt.Errorf("scheme already registered %s", scheme)
|
|
||||||
}
|
|
||||||
log.WithField("name", scheme).Debug("Registering discovery service")
|
|
||||||
discoveries[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, discoveryOpt map[string]string) (Discovery, error) {
|
|
||||||
scheme, uri := parse(rawurl)
|
|
||||||
|
|
||||||
if discovery, exists := discoveries[scheme]; exists {
|
|
||||||
log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service")
|
|
||||||
err := discovery.Initialize(uri, heartbeat, ttl, discoveryOpt)
|
|
||||||
return discovery, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ErrNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
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))
|
|
||||||
}
|
|
|
@ -1,106 +0,0 @@
|
||||||
package file
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/swarm/discovery"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInitialize(t *testing.T) {
|
|
||||||
d := &Discovery{}
|
|
||||||
d.Initialize("/path/to/file", 1000, 0, nil)
|
|
||||||
assert.Equal(t, d.path, "/path/to/file")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
|
||||||
d, err := discovery.New("file:///path/to/file", 0, 0, nil)
|
|
||||||
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, nil)
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,199 +0,0 @@
|
||||||
package kv
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/libkv"
|
|
||||||
"github.com/docker/libkv/store"
|
|
||||||
libkvmock "github.com/docker/libkv/store/mock"
|
|
||||||
"github.com/docker/swarm/discovery"
|
|
||||||
"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, nil)
|
|
||||||
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, defaultDiscoveryPath)
|
|
||||||
|
|
||||||
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, nil)
|
|
||||||
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/"+defaultDiscoveryPath)
|
|
||||||
|
|
||||||
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, nil)
|
|
||||||
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/"+defaultDiscoveryPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInitializeWithCerts(t *testing.T) {
|
|
||||||
cert := `-----BEGIN CERTIFICATE-----
|
|
||||||
MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT
|
|
||||||
B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD
|
|
||||||
VQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wRC
|
|
||||||
O+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4+zE9h80aC4hz+6caRpds
|
|
||||||
+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhRSoSi3nY+B7F2E8cuz14q
|
|
||||||
V2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZrpXUyXxAvzXfpFXo1RhSb
|
|
||||||
UywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUerVYrCPq8vqfn//01qz55
|
|
||||||
Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHojxOpXTBepUCIJLbtNnWFT
|
|
||||||
V44t9gh5IqIWtoBReQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/
|
|
||||||
BAgwBgEB/wIBAjAdBgNVHQ4EFgQUZKUI8IIjIww7X/6hvwggQK4bD24wHwYDVR0j
|
|
||||||
BBgwFoAUZKUI8IIjIww7X/6hvwggQK4bD24wCwYJKoZIhvcNAQELA4IBAQDES2cz
|
|
||||||
7sCQfDCxCIWH7X8kpi/JWExzUyQEJ0rBzN1m3/x8ySRxtXyGekimBqQwQdFqlwMI
|
|
||||||
xzAQKkh3ue8tNSzRbwqMSyH14N1KrSxYS9e9szJHfUasoTpQGPmDmGIoRJuq1h6M
|
|
||||||
ej5x1SCJ7GWCR6xEXKUIE9OftXm9TdFzWa7Ja3OHz/mXteii8VXDuZ5ACq6EE5bY
|
|
||||||
8sP4gcICfJ5fTrpTlk9FIqEWWQrCGa5wk95PGEj+GJpNogjXQ97wVoo/Y3p1brEn
|
|
||||||
t5zjN9PAq4H1fuCMdNNA+p1DHNwd+ELTxcMAnb2ajwHvV6lKPXutrTFc4umJToBX
|
|
||||||
FpTxDmJHEV4bzUzh
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
`
|
|
||||||
key := `-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIEpQIBAAKCAQEA1wRCO+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4
|
|
||||||
+zE9h80aC4hz+6caRpds+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhR
|
|
||||||
SoSi3nY+B7F2E8cuz14qV2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZr
|
|
||||||
pXUyXxAvzXfpFXo1RhSbUywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUe
|
|
||||||
rVYrCPq8vqfn//01qz55Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHoj
|
|
||||||
xOpXTBepUCIJLbtNnWFTV44t9gh5IqIWtoBReQIDAQABAoIBAHSWipORGp/uKFXj
|
|
||||||
i/mut776x8ofsAxhnLBARQr93ID+i49W8H7EJGkOfaDjTICYC1dbpGrri61qk8sx
|
|
||||||
qX7p3v/5NzKwOIfEpirgwVIqSNYe/ncbxnhxkx6tXtUtFKmEx40JskvSpSYAhmmO
|
|
||||||
1XSx0E/PWaEN/nLgX/f1eWJIlxlQkk3QeqL+FGbCXI48DEtlJ9+MzMu4pAwZTpj5
|
|
||||||
5qtXo5JJ0jRGfJVPAOznRsYqv864AhMdMIWguzk6EGnbaCWwPcfcn+h9a5LMdony
|
|
||||||
MDHfBS7bb5tkF3+AfnVY3IBMVx7YlsD9eAyajlgiKu4zLbwTRHjXgShy+4Oussz0
|
|
||||||
ugNGnkECgYEA/hi+McrZC8C4gg6XqK8+9joD8tnyDZDz88BQB7CZqABUSwvjDqlP
|
|
||||||
L8hcwo/lzvjBNYGkqaFPUICGWKjeCtd8pPS2DCVXxDQX4aHF1vUur0uYNncJiV3N
|
|
||||||
XQz4Iemsa6wnKf6M67b5vMXICw7dw0HZCdIHD1hnhdtDz0uVpeevLZ8CgYEA2KCT
|
|
||||||
Y43lorjrbCgMqtlefkr3GJA9dey+hTzCiWEOOqn9RqGoEGUday0sKhiLofOgmN2B
|
|
||||||
LEukpKIey8s+Q/cb6lReajDVPDsMweX8i7hz3Wa4Ugp4Xa5BpHqu8qIAE2JUZ7bU
|
|
||||||
t88aQAYE58pUF+/Lq1QzAQdrjjzQBx6SrBxieecCgYEAvukoPZEC8mmiN1VvbTX+
|
|
||||||
QFHmlZha3QaDxChB+QUe7bMRojEUL/fVnzkTOLuVFqSfxevaI/km9n0ac5KtAchV
|
|
||||||
xjp2bTnBb5EUQFqjopYktWA+xO07JRJtMfSEmjZPbbay1kKC7rdTfBm961EIHaRj
|
|
||||||
xZUf6M+rOE8964oGrdgdLlECgYEA046GQmx6fh7/82FtdZDRQp9tj3SWQUtSiQZc
|
|
||||||
qhO59Lq8mjUXz+MgBuJXxkiwXRpzlbaFB0Bca1fUoYw8o915SrDYf/Zu2OKGQ/qa
|
|
||||||
V81sgiVmDuEgycR7YOlbX6OsVUHrUlpwhY3hgfMe6UtkMvhBvHF/WhroBEIJm1pV
|
|
||||||
PXZ/CbMCgYEApNWVktFBjOaYfY6SNn4iSts1jgsQbbpglg3kT7PLKjCAhI6lNsbk
|
|
||||||
dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL
|
|
||||||
BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I=
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
`
|
|
||||||
certFile, err := ioutil.TempFile("", "cert")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
defer os.Remove(certFile.Name())
|
|
||||||
certFile.Write([]byte(cert))
|
|
||||||
certFile.Close()
|
|
||||||
keyFile, err := ioutil.TempFile("", "key")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
defer os.Remove(keyFile.Name())
|
|
||||||
keyFile.Write([]byte(key))
|
|
||||||
keyFile.Close()
|
|
||||||
|
|
||||||
libkv.AddStore("mock", libkvmock.New)
|
|
||||||
d := &Discovery{backend: "mock"}
|
|
||||||
err = d.Initialize("127.0.0.3:1234", 0, 0, map[string]string{
|
|
||||||
"kv.cacertfile": certFile.Name(),
|
|
||||||
"kv.certfile": certFile.Name(),
|
|
||||||
"kv.keyfile": keyFile.Name(),
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
s := d.store.(*libkvmock.Mock)
|
|
||||||
assert.Equal(t, s.Options.ClientTLS.CACertFile, certFile.Name())
|
|
||||||
assert.Equal(t, s.Options.ClientTLS.CertFile, certFile.Name())
|
|
||||||
assert.Equal(t, s.Options.ClientTLS.KeyFile, keyFile.Name())
|
|
||||||
}
|
|
||||||
|
|
||||||
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, nil)
|
|
||||||
d.store = storeMock
|
|
||||||
|
|
||||||
s := d.store.(*libkvmock.Mock)
|
|
||||||
mockCh := make(chan []*store.KVPair)
|
|
||||||
|
|
||||||
// The first watch will fail on those three calls
|
|
||||||
s.On("Exists", "path/"+defaultDiscoveryPath).Return(false, errors.New("test error"))
|
|
||||||
s.On("Put", "path/"+defaultDiscoveryPath, mock.Anything, mock.Anything).Return(errors.New("test error"))
|
|
||||||
s.On("WatchTree", "path/"+defaultDiscoveryPath, mock.Anything).Return(mockCh, errors.New("test error")).Once()
|
|
||||||
|
|
||||||
// The second one will succeed.
|
|
||||||
s.On("WatchTree", "path/"+defaultDiscoveryPath, 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", defaultDiscoveryPath, "1.1.1.1"), Value: []byte("1.1.1.1:1111")},
|
|
||||||
{Key: path.Join("path", defaultDiscoveryPath, "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", defaultDiscoveryPath, "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/"+defaultDiscoveryPath, 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)
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package nodes
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/swarm/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, nil)
|
|
||||||
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, nil)
|
|
||||||
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, nil)
|
|
||||||
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"))
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/swarm/discovery"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
const discoveryURL = "https://discovery.hub.docker.com/v1"
|
const discoveryURL = "https://discovery.hub.docker.com/v1"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/swarm/discovery"
|
"github.com/docker/docker/pkg/discovery"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -209,8 +209,9 @@ swarm is connected to the public internet. To create your swarm:
|
||||||
|
|
||||||
You can contribute a new discovery backend to Swarm. For information on how to
|
You can contribute a new discovery backend to Swarm. For information on how to
|
||||||
do this, see <a
|
do this, see <a
|
||||||
href="https://github.com/docker/swarm/blob/master/discovery/README.md">our
|
href="https://github.com/docker/docker/tree/master/pkg/discovery">
|
||||||
discovery README in the Docker Swarm repository</a>.
|
github.com/docker/docker/pkg/discovery</a>.
|
||||||
|
|
||||||
|
|
||||||
## Docker Swarm documentation index
|
## Docker Swarm documentation index
|
||||||
|
|
||||||
|
|
6
main.go
6
main.go
|
@ -1,9 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "github.com/docker/swarm/discovery/file"
|
_ "github.com/docker/docker/pkg/discovery/file"
|
||||||
_ "github.com/docker/swarm/discovery/kv"
|
_ "github.com/docker/docker/pkg/discovery/kv"
|
||||||
_ "github.com/docker/swarm/discovery/nodes"
|
_ "github.com/docker/docker/pkg/discovery/nodes"
|
||||||
_ "github.com/docker/swarm/discovery/token"
|
_ "github.com/docker/swarm/discovery/token"
|
||||||
|
|
||||||
"github.com/docker/swarm/cli"
|
"github.com/docker/swarm/cli"
|
||||||
|
|
Loading…
Reference in New Issue