commit
14b5a50f0a
130 changed files with 7391 additions and 943 deletions
|
@ -33,8 +33,8 @@ import (
|
|||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/drivers/bridge"
|
||||
"github.com/docker/libnetwork/ipamutils"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/options"
|
||||
lntypes "github.com/docker/libnetwork/types"
|
||||
"github.com/opencontainers/runc/libcontainer/label"
|
||||
|
@ -594,12 +594,12 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC
|
|||
}
|
||||
|
||||
// Initialize default network on "null"
|
||||
if _, err := controller.NewNetwork("null", "none", libnetwork.NetworkOptionPersist(false)); err != nil {
|
||||
if _, err := controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false)); err != nil {
|
||||
return nil, fmt.Errorf("Error creating default \"null\" network: %v", err)
|
||||
}
|
||||
|
||||
// Initialize default network on "host"
|
||||
if _, err := controller.NewNetwork("host", "host", libnetwork.NetworkOptionPersist(false)); err != nil {
|
||||
if _, err := controller.NewNetwork("host", "host", "", libnetwork.NetworkOptionPersist(false)); err != nil {
|
||||
return nil, fmt.Errorf("Error creating default \"host\" network: %v", err)
|
||||
}
|
||||
|
||||
|
@ -656,7 +656,7 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
|||
|
||||
ipamV4Conf = &libnetwork.IpamConf{AuxAddresses: make(map[string]string)}
|
||||
|
||||
nw, nw6List, err := ipamutils.ElectInterfaceAddresses(bridgeName)
|
||||
nw, nw6List, err := netutils.ElectInterfaceAddresses(bridgeName)
|
||||
if err == nil {
|
||||
ipamV4Conf.PreferredPool = lntypes.GetIPNetCanonical(nw).String()
|
||||
hip, _ := lntypes.GetHostPartIP(nw.IP, nw.Mask)
|
||||
|
@ -734,7 +734,7 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
|||
v6Conf = append(v6Conf, ipamV6Conf)
|
||||
}
|
||||
// Initialize default network on "bridge" with the same name
|
||||
_, err = controller.NewNetwork("bridge", "bridge",
|
||||
_, err = controller.NewNetwork("bridge", "bridge", "",
|
||||
libnetwork.NetworkOptionEnableIPv6(config.bridgeConfig.EnableIPv6),
|
||||
libnetwork.NetworkOptionDriverOpts(netOption),
|
||||
libnetwork.NetworkOptionIpam("default", "", v4Conf, v6Conf, nil),
|
||||
|
|
|
@ -221,7 +221,7 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC
|
|||
}
|
||||
}
|
||||
|
||||
_, err = controller.NewNetwork("null", "none", libnetwork.NetworkOptionPersist(false))
|
||||
_, err = controller.NewNetwork("null", "none", "", libnetwork.NetworkOptionPersist(false))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -266,7 +266,7 @@ func (daemon *Daemon) initNetworkController(config *Config) (libnetwork.NetworkC
|
|||
}
|
||||
|
||||
v6Conf := []*libnetwork.IpamConf{}
|
||||
_, err := controller.NewNetwork(strings.ToLower(v.Type), name,
|
||||
_, err := controller.NewNetwork(strings.ToLower(v.Type), name, "",
|
||||
libnetwork.NetworkOptionGeneric(options.Generic{
|
||||
netlabel.GenericData: netOption,
|
||||
}),
|
||||
|
@ -307,7 +307,7 @@ func initBridgeDriver(controller libnetwork.NetworkController, config *Config) e
|
|||
v4Conf := []*libnetwork.IpamConf{&ipamV4Conf}
|
||||
v6Conf := []*libnetwork.IpamConf{}
|
||||
|
||||
_, err := controller.NewNetwork(string(runconfig.DefaultDaemonNetworkMode()), runconfig.DefaultDaemonNetworkMode().NetworkName(),
|
||||
_, err := controller.NewNetwork(string(runconfig.DefaultDaemonNetworkMode()), runconfig.DefaultDaemonNetworkMode().NetworkName(), "",
|
||||
libnetwork.NetworkOptionGeneric(options.Generic{
|
||||
netlabel.GenericData: netOption,
|
||||
}),
|
||||
|
|
|
@ -134,7 +134,7 @@ func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.N
|
|||
if create.Internal {
|
||||
nwOptions = append(nwOptions, libnetwork.NetworkOptionInternalNetwork())
|
||||
}
|
||||
n, err := c.NewNetwork(driver, create.Name, nwOptions...)
|
||||
n, err := c.NewNetwork(driver, create.Name, "", nwOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ source 'hack/.vendor-helpers.sh'
|
|||
clone git github.com/Azure/go-ansiterm 388960b655244e76e24c75f48631564eaefade62
|
||||
clone git github.com/Microsoft/hcsshim v0.2.2
|
||||
clone git github.com/Microsoft/go-winio v0.3.4
|
||||
clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps
|
||||
clone git github.com/Sirupsen/logrus v0.10.0 # logrus is a common dependency among multiple deps
|
||||
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a
|
||||
clone git github.com/go-check/check 03a4d9dcf2f92eae8e90ed42aa2656f63fdd0b14 https://github.com/cpuguy83/check.git
|
||||
clone git github.com/gorilla/context 14f550f51a
|
||||
|
@ -29,12 +29,15 @@ clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
|
|||
clone git github.com/imdario/mergo 0.2.1
|
||||
|
||||
#get libnetwork packages
|
||||
clone git github.com/docker/libnetwork v0.8.0-dev.1
|
||||
clone git github.com/docker/libnetwork b66c0385f30c6aa27b2957ed1072682c19a0b0b4
|
||||
clone git github.com/docker/go-events 2e7d352816128aa84f4d29b2a21d400133701a0d
|
||||
clone git github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
|
||||
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
||||
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
|
||||
clone git github.com/hashicorp/serf 7151adcef72687bf95f451a2e0ba15cb19412bf2
|
||||
clone git github.com/docker/libkv c2aac5dbbaa5c872211edea7c0f32b3bd67e7410
|
||||
clone git github.com/hashicorp/memberlist 88ac4de0d1a0ca6def284b571342db3b777a4c37
|
||||
clone git github.com/hashicorp/go-multierror fcdddc395df1ddf4247c69bd436e84cfa0733f7e
|
||||
clone git github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
|
||||
clone git github.com/docker/libkv 7283ef27ed32fe267388510a91709b307bb9942c
|
||||
clone git github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
|
||||
clone git github.com/vishvananda/netlink 631962935bff4f3d20ff32a72e8944f6d2836a26
|
||||
clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
|
||||
|
|
|
@ -24,8 +24,8 @@ import (
|
|||
"github.com/docker/docker/pkg/stringutils"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/go-check/check"
|
||||
libcontainerUser "github.com/opencontainers/runc/libcontainer/user"
|
||||
)
|
||||
|
@ -1308,13 +1308,13 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
|
|||
c.Fatalf("/etc/resolv.conf does not exist")
|
||||
}
|
||||
|
||||
hostNamservers := resolvconf.GetNameservers(origResolvConf, netutils.IP)
|
||||
hostNamservers := resolvconf.GetNameservers(origResolvConf, types.IP)
|
||||
hostSearch := resolvconf.GetSearchDomains(origResolvConf)
|
||||
|
||||
var out string
|
||||
out, _ = dockerCmd(c, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf")
|
||||
|
||||
if actualNameservers := resolvconf.GetNameservers([]byte(out), netutils.IP); string(actualNameservers[0]) != "127.0.0.1" {
|
||||
if actualNameservers := resolvconf.GetNameservers([]byte(out), types.IP); string(actualNameservers[0]) != "127.0.0.1" {
|
||||
c.Fatalf("expected '127.0.0.1', but says: %q", string(actualNameservers[0]))
|
||||
}
|
||||
|
||||
|
@ -1330,7 +1330,7 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
|
|||
|
||||
out, _ = dockerCmd(c, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf")
|
||||
|
||||
actualNameservers := resolvconf.GetNameservers([]byte(out), netutils.IP)
|
||||
actualNameservers := resolvconf.GetNameservers([]byte(out), types.IP)
|
||||
if len(actualNameservers) != len(hostNamservers) {
|
||||
c.Fatalf("expected %q nameserver(s), but it has: %q", len(hostNamservers), len(actualNameservers))
|
||||
}
|
||||
|
@ -1361,11 +1361,11 @@ func (s *DockerSuite) TestRunDnsOptionsBasedOnHostResolvConf(c *check.C) {
|
|||
c.Fatalf("/etc/resolv.conf does not exist")
|
||||
}
|
||||
|
||||
hostNamservers = resolvconf.GetNameservers(resolvConf, netutils.IP)
|
||||
hostNamservers = resolvconf.GetNameservers(resolvConf, types.IP)
|
||||
hostSearch = resolvconf.GetSearchDomains(resolvConf)
|
||||
|
||||
out, _ = dockerCmd(c, "run", "busybox", "cat", "/etc/resolv.conf")
|
||||
if actualNameservers = resolvconf.GetNameservers([]byte(out), netutils.IP); string(actualNameservers[0]) != "12.34.56.78" || len(actualNameservers) != 1 {
|
||||
if actualNameservers = resolvconf.GetNameservers([]byte(out), types.IP); string(actualNameservers[0]) != "12.34.56.78" || len(actualNameservers) != 1 {
|
||||
c.Fatalf("expected '12.34.56.78', but has: %v", actualNameservers)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
# 0.10.0
|
||||
|
||||
* feature: Add a test hook (#180)
|
||||
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||
* performance: avoid re-allocations on `WithFields` (#335)
|
||||
|
||||
# 0.9.0
|
||||
|
||||
* logrus/text_formatter: don't emit empty msg
|
||||
|
|
30
vendor/src/github.com/Sirupsen/logrus/README.md
vendored
30
vendor/src/github.com/Sirupsen/logrus/README.md
vendored
|
@ -1,4 +1,4 @@
|
|||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/Sirupsen/logrus) [][godoc]
|
||||
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://travis-ci.org/Sirupsen/logrus) [](https://godoc.org/github.com/Sirupsen/logrus)
|
||||
|
||||
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||
the standard library logger. [Godoc][godoc]. **Please note the Logrus API is not
|
||||
|
@ -12,7 +12,7 @@ plain text):
|
|||
|
||||

|
||||
|
||||
With `log.Formatter = new(logrus.JSONFormatter)`, for easy parsing by logstash
|
||||
With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash
|
||||
or Splunk:
|
||||
|
||||
```json
|
||||
|
@ -32,7 +32,7 @@ ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
|||
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||
```
|
||||
|
||||
With the default `log.Formatter = new(&log.TextFormatter{})` when a TTY is not
|
||||
With the default `log.SetFormatter(&log.TextFormatter{})` when a TTY is not
|
||||
attached, the output is compatible with the
|
||||
[logfmt](http://godoc.org/github.com/kr/logfmt) format:
|
||||
|
||||
|
@ -222,6 +222,11 @@ Note: Syslog hook also support connecting to local syslog (Ex. "/dev/log" or "/v
|
|||
| [Octokit](https://github.com/dorajistyle/logrus-octokit-hook) | Hook for logging to github via octokit |
|
||||
| [DeferPanic](https://github.com/deferpanic/dp-logrus) | Hook for logging to DeferPanic |
|
||||
| [Redis-Hook](https://github.com/rogierlommers/logrus-redis-hook) | Hook for logging to a ELK stack (through Redis) |
|
||||
| [Amqp-Hook](https://github.com/vladoatanasov/logrus_amqp) | Hook for logging to Amqp broker (Like RabbitMQ) |
|
||||
| [KafkaLogrus](https://github.com/goibibo/KafkaLogrus) | Hook for logging to kafka |
|
||||
| [Typetalk](https://github.com/dragon3/logrus-typetalk-hook) | Hook for logging to [Typetalk](https://www.typetalk.in/) |
|
||||
| [ElasticSearch](https://github.com/sohlich/elogrus) | Hook for logging to ElasticSearch|
|
||||
|
||||
|
||||
#### Level logging
|
||||
|
||||
|
@ -363,4 +368,21 @@ entries. It should not be a feature of the application-level logger.
|
|||
| ---- | ----------- |
|
||||
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will generated with different config at different environment.|
|
||||
|
||||
[godoc]: https://godoc.org/github.com/Sirupsen/logrus
|
||||
#### Testing
|
||||
|
||||
Logrus has a built in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||
|
||||
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just add the `test` hook
|
||||
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||
|
||||
```go
|
||||
logger, hook := NewNullLogger()
|
||||
logger.Error("Hello error")
|
||||
|
||||
assert.Equal(1, len(hook.Entries))
|
||||
assert.Equal(logrus.ErrorLevel, hook.LastEntry().Level)
|
||||
assert.Equal("Hello error", hook.LastEntry().Message)
|
||||
|
||||
hook.Reset()
|
||||
assert.Nil(hook.LastEntry())
|
||||
```
|
||||
|
|
|
@ -68,7 +68,7 @@ func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
|||
|
||||
// Add a map of fields to the Entry.
|
||||
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
data := Fields{}
|
||||
data := make(Fields, len(entry.Data)+len(fields))
|
||||
for k, v := range entry.Data {
|
||||
data[k] = v
|
||||
}
|
||||
|
|
47
vendor/src/github.com/Sirupsen/logrus/logrus.go
vendored
47
vendor/src/github.com/Sirupsen/logrus/logrus.go
vendored
|
@ -3,6 +3,7 @@ package logrus
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Fields type, used to pass to `WithFields`.
|
||||
|
@ -33,7 +34,7 @@ func (level Level) String() string {
|
|||
|
||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||
func ParseLevel(lvl string) (Level, error) {
|
||||
switch lvl {
|
||||
switch strings.ToLower(lvl) {
|
||||
case "panic":
|
||||
return PanicLevel, nil
|
||||
case "fatal":
|
||||
|
@ -52,6 +53,16 @@ func ParseLevel(lvl string) (Level, error) {
|
|||
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||
}
|
||||
|
||||
// A constant exposing all logging levels
|
||||
var AllLevels = []Level{
|
||||
PanicLevel,
|
||||
FatalLevel,
|
||||
ErrorLevel,
|
||||
WarnLevel,
|
||||
InfoLevel,
|
||||
DebugLevel,
|
||||
}
|
||||
|
||||
// These are the different logging levels. You can set the logging level to log
|
||||
// on your instance of logger, obtained with `logrus.New()`.
|
||||
const (
|
||||
|
@ -96,3 +107,37 @@ type StdLogger interface {
|
|||
Panicf(string, ...interface{})
|
||||
Panicln(...interface{})
|
||||
}
|
||||
|
||||
// The FieldLogger interface generalizes the Entry and Logger types
|
||||
type FieldLogger interface {
|
||||
WithField(key string, value interface{}) *Entry
|
||||
WithFields(fields Fields) *Entry
|
||||
WithError(err error) *Entry
|
||||
|
||||
Debugf(format string, args ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
Warningf(format string, args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
Panicf(format string, args ...interface{})
|
||||
|
||||
Debug(args ...interface{})
|
||||
Info(args ...interface{})
|
||||
Print(args ...interface{})
|
||||
Warn(args ...interface{})
|
||||
Warning(args ...interface{})
|
||||
Error(args ...interface{})
|
||||
Fatal(args ...interface{})
|
||||
Panic(args ...interface{})
|
||||
|
||||
Debugln(args ...interface{})
|
||||
Infoln(args ...interface{})
|
||||
Println(args ...interface{})
|
||||
Warnln(args ...interface{})
|
||||
Warningln(args ...interface{})
|
||||
Errorln(args ...interface{})
|
||||
Fatalln(args ...interface{})
|
||||
Panicln(args ...interface{})
|
||||
}
|
||||
|
|
22
vendor/src/github.com/armon/go-radix/.gitignore
vendored
Normal file
22
vendor/src/github.com/armon/go-radix/.gitignore
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
3
vendor/src/github.com/armon/go-radix/.travis.yml
vendored
Normal file
3
vendor/src/github.com/armon/go-radix/.travis.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
language: go
|
||||
go:
|
||||
- tip
|
20
vendor/src/github.com/armon/go-radix/LICENSE
vendored
Normal file
20
vendor/src/github.com/armon/go-radix/LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Armon Dadgar
|
||||
|
||||
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.
|
36
vendor/src/github.com/armon/go-radix/README.md
vendored
Normal file
36
vendor/src/github.com/armon/go-radix/README.md
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
go-radix [](https://travis-ci.org/armon/go-radix)
|
||||
=========
|
||||
|
||||
Provides the `radix` package that implements a [radix tree](http://en.wikipedia.org/wiki/Radix_tree).
|
||||
The package only provides a single `Tree` implementation, optimized for sparse nodes.
|
||||
|
||||
As a radix tree, it provides the following:
|
||||
* O(k) operations. In many cases, this can be faster than a hash table since
|
||||
the hash function is an O(k) operation, and hash tables have very poor cache locality.
|
||||
* Minimum / Maximum value lookups
|
||||
* Ordered iteration
|
||||
|
||||
Documentation
|
||||
=============
|
||||
|
||||
The full documentation is available on [Godoc](http://godoc.org/github.com/armon/go-radix).
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
Below is a simple example of usage
|
||||
|
||||
```go
|
||||
// Create a tree
|
||||
r := radix.New()
|
||||
r.Insert("foo", 1)
|
||||
r.Insert("bar", 2)
|
||||
r.Insert("foobar", 2)
|
||||
|
||||
// Find the longest prefix match
|
||||
m, _, _ := r.LongestPrefix("foozip")
|
||||
if m != "foo" {
|
||||
panic("should be foo")
|
||||
}
|
||||
```
|
||||
|
467
vendor/src/github.com/armon/go-radix/radix.go
vendored
Normal file
467
vendor/src/github.com/armon/go-radix/radix.go
vendored
Normal file
|
@ -0,0 +1,467 @@
|
|||
package radix
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// WalkFn is used when walking the tree. Takes a
|
||||
// key and value, returning if iteration should
|
||||
// be terminated.
|
||||
type WalkFn func(s string, v interface{}) bool
|
||||
|
||||
// leafNode is used to represent a value
|
||||
type leafNode struct {
|
||||
key string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
// edge is used to represent an edge node
|
||||
type edge struct {
|
||||
label byte
|
||||
node *node
|
||||
}
|
||||
|
||||
type node struct {
|
||||
// leaf is used to store possible leaf
|
||||
leaf *leafNode
|
||||
|
||||
// prefix is the common prefix we ignore
|
||||
prefix string
|
||||
|
||||
// Edges should be stored in-order for iteration.
|
||||
// We avoid a fully materialized slice to save memory,
|
||||
// since in most cases we expect to be sparse
|
||||
edges edges
|
||||
}
|
||||
|
||||
func (n *node) isLeaf() bool {
|
||||
return n.leaf != nil
|
||||
}
|
||||
|
||||
func (n *node) addEdge(e edge) {
|
||||
n.edges = append(n.edges, e)
|
||||
n.edges.Sort()
|
||||
}
|
||||
|
||||
func (n *node) replaceEdge(e edge) {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= e.label
|
||||
})
|
||||
if idx < num && n.edges[idx].label == e.label {
|
||||
n.edges[idx].node = e.node
|
||||
return
|
||||
}
|
||||
panic("replacing missing edge")
|
||||
}
|
||||
|
||||
func (n *node) getEdge(label byte) *node {
|
||||
num := len(n.edges)
|
||||
idx := sort.Search(num, func(i int) bool {
|
||||
return n.edges[i].label >= label
|
||||
})
|
||||
if idx < num && n.edges[idx].label == label {
|
||||
return n.edges[idx].node
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type edges []edge
|
||||
|
||||
func (e edges) Len() int {
|
||||
return len(e)
|
||||
}
|
||||
|
||||
func (e edges) Less(i, j int) bool {
|
||||
return e[i].label < e[j].label
|
||||
}
|
||||
|
||||
func (e edges) Swap(i, j int) {
|
||||
e[i], e[j] = e[j], e[i]
|
||||
}
|
||||
|
||||
func (e edges) Sort() {
|
||||
sort.Sort(e)
|
||||
}
|
||||
|
||||
// Tree implements a radix tree. This can be treated as a
|
||||
// Dictionary abstract data type. The main advantage over
|
||||
// a standard hash map is prefix-based lookups and
|
||||
// ordered iteration,
|
||||
type Tree struct {
|
||||
root *node
|
||||
size int
|
||||
}
|
||||
|
||||
// New returns an empty Tree
|
||||
func New() *Tree {
|
||||
return NewFromMap(nil)
|
||||
}
|
||||
|
||||
// NewFromMap returns a new tree containing the keys
|
||||
// from an existing map
|
||||
func NewFromMap(m map[string]interface{}) *Tree {
|
||||
t := &Tree{root: &node{}}
|
||||
for k, v := range m {
|
||||
t.Insert(k, v)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// Len is used to return the number of elements in the tree
|
||||
func (t *Tree) Len() int {
|
||||
return t.size
|
||||
}
|
||||
|
||||
// longestPrefix finds the length of the shared prefix
|
||||
// of two strings
|
||||
func longestPrefix(k1, k2 string) int {
|
||||
max := len(k1)
|
||||
if l := len(k2); l < max {
|
||||
max = l
|
||||
}
|
||||
var i int
|
||||
for i = 0; i < max; i++ {
|
||||
if k1[i] != k2[i] {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Insert is used to add a newentry or update
|
||||
// an existing entry. Returns if updated.
|
||||
func (t *Tree) Insert(s string, v interface{}) (interface{}, bool) {
|
||||
var parent *node
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Handle key exhaution
|
||||
if len(search) == 0 {
|
||||
if n.isLeaf() {
|
||||
old := n.leaf.val
|
||||
n.leaf.val = v
|
||||
return old, true
|
||||
} else {
|
||||
n.leaf = &leafNode{
|
||||
key: s,
|
||||
val: v,
|
||||
}
|
||||
t.size++
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// Look for the edge
|
||||
parent = n
|
||||
n = n.getEdge(search[0])
|
||||
|
||||
// No edge, create one
|
||||
if n == nil {
|
||||
e := edge{
|
||||
label: search[0],
|
||||
node: &node{
|
||||
leaf: &leafNode{
|
||||
key: s,
|
||||
val: v,
|
||||
},
|
||||
prefix: search,
|
||||
},
|
||||
}
|
||||
parent.addEdge(e)
|
||||
t.size++
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Determine longest prefix of the search key on match
|
||||
commonPrefix := longestPrefix(search, n.prefix)
|
||||
if commonPrefix == len(n.prefix) {
|
||||
search = search[commonPrefix:]
|
||||
continue
|
||||
}
|
||||
|
||||
// Split the node
|
||||
t.size++
|
||||
child := &node{
|
||||
prefix: search[:commonPrefix],
|
||||
}
|
||||
parent.replaceEdge(edge{
|
||||
label: search[0],
|
||||
node: child,
|
||||
})
|
||||
|
||||
// Restore the existing node
|
||||
child.addEdge(edge{
|
||||
label: n.prefix[commonPrefix],
|
||||
node: n,
|
||||
})
|
||||
n.prefix = n.prefix[commonPrefix:]
|
||||
|
||||
// Create a new leaf node
|
||||
leaf := &leafNode{
|
||||
key: s,
|
||||
val: v,
|
||||
}
|
||||
|
||||
// If the new key is a subset, add to to this node
|
||||
search = search[commonPrefix:]
|
||||
if len(search) == 0 {
|
||||
child.leaf = leaf
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Create a new edge for the node
|
||||
child.addEdge(edge{
|
||||
label: search[0],
|
||||
node: &node{
|
||||
leaf: leaf,
|
||||
prefix: search,
|
||||
},
|
||||
})
|
||||
return nil, false
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Delete is used to delete a key, returning the previous
|
||||
// value and if it was deleted
|
||||
func (t *Tree) Delete(s string) (interface{}, bool) {
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
if !n.isLeaf() {
|
||||
break
|
||||
}
|
||||
goto DELETE
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
|
||||
DELETE:
|
||||
// Delete the leaf
|
||||
leaf := n.leaf
|
||||
n.leaf = nil
|
||||
t.size--
|
||||
|
||||
// Check if we should merge this node
|
||||
if len(n.edges) == 1 {
|
||||
e := n.edges[0]
|
||||
child := e.node
|
||||
n.prefix = n.prefix + child.prefix
|
||||
n.leaf = child.leaf
|
||||
n.edges = child.edges
|
||||
}
|
||||
return leaf.val, true
|
||||
}
|
||||
|
||||
// Get is used to lookup a specific key, returning
|
||||
// the value and if it was found
|
||||
func (t *Tree) Get(s string) (interface{}, bool) {
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
if n.isLeaf() {
|
||||
return n.leaf.val, true
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// LongestPrefix is like Get, but instead of an
|
||||
// exact match, it will return the longest prefix match.
|
||||
func (t *Tree) LongestPrefix(s string) (string, interface{}, bool) {
|
||||
var last *leafNode
|
||||
n := t.root
|
||||
search := s
|
||||
for {
|
||||
// Look for a leaf node
|
||||
if n.isLeaf() {
|
||||
last = n.leaf
|
||||
}
|
||||
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if last != nil {
|
||||
return last.key, last.val, true
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// Minimum is used to return the minimum value in the tree
|
||||
func (t *Tree) Minimum() (string, interface{}, bool) {
|
||||
n := t.root
|
||||
for {
|
||||
if n.isLeaf() {
|
||||
return n.leaf.key, n.leaf.val, true
|
||||
}
|
||||
if len(n.edges) > 0 {
|
||||
n = n.edges[0].node
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// Maximum is used to return the maximum value in the tree
|
||||
func (t *Tree) Maximum() (string, interface{}, bool) {
|
||||
n := t.root
|
||||
for {
|
||||
if num := len(n.edges); num > 0 {
|
||||
n = n.edges[num-1].node
|
||||
continue
|
||||
}
|
||||
if n.isLeaf() {
|
||||
return n.leaf.key, n.leaf.val, true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
// Walk is used to walk the tree
|
||||
func (t *Tree) Walk(fn WalkFn) {
|
||||
recursiveWalk(t.root, fn)
|
||||
}
|
||||
|
||||
// WalkPrefix is used to walk the tree under a prefix
|
||||
func (t *Tree) WalkPrefix(prefix string, fn WalkFn) {
|
||||
n := t.root
|
||||
search := prefix
|
||||
for {
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
recursiveWalk(n, fn)
|
||||
return
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
|
||||
} else if strings.HasPrefix(n.prefix, search) {
|
||||
// Child may be under our search prefix
|
||||
recursiveWalk(n, fn)
|
||||
return
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// WalkPath is used to walk the tree, but only visiting nodes
|
||||
// from the root down to a given leaf. Where WalkPrefix walks
|
||||
// all the entries *under* the given prefix, this walks the
|
||||
// entries *above* the given prefix.
|
||||
func (t *Tree) WalkPath(path string, fn WalkFn) {
|
||||
n := t.root
|
||||
search := path
|
||||
for {
|
||||
// Visit the leaf values if any
|
||||
if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
|
||||
return
|
||||
}
|
||||
|
||||
// Check for key exhaution
|
||||
if len(search) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Look for an edge
|
||||
n = n.getEdge(search[0])
|
||||
if n == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Consume the search prefix
|
||||
if strings.HasPrefix(search, n.prefix) {
|
||||
search = search[len(n.prefix):]
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recursiveWalk is used to do a pre-order walk of a node
|
||||
// recursively. Returns true if the walk should be aborted
|
||||
func recursiveWalk(n *node, fn WalkFn) bool {
|
||||
// Visit the leaf values if any
|
||||
if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Recurse on the children
|
||||
for _, e := range n.edges {
|
||||
if recursiveWalk(e.node, fn) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ToMap is used to walk the tree and convert it into a map
|
||||
func (t *Tree) ToMap() map[string]interface{} {
|
||||
out := make(map[string]interface{}, t.size)
|
||||
t.Walk(func(k string, v interface{}) bool {
|
||||
out[k] = v
|
||||
return false
|
||||
})
|
||||
return out
|
||||
}
|
24
vendor/src/github.com/docker/go-events/.gitignore
vendored
Normal file
24
vendor/src/github.com/docker/go-events/.gitignore
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
201
vendor/src/github.com/docker/go-events/LICENSE
vendored
Normal file
201
vendor/src/github.com/docker/go-events/LICENSE
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://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
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
112
vendor/src/github.com/docker/go-events/README.md
vendored
Normal file
112
vendor/src/github.com/docker/go-events/README.md
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
# Docker Events Package
|
||||
|
||||
[](https://godoc.org/github.com/docker/go-events)
|
||||
[](https://circleci.com/gh/docker/go-events)
|
||||
|
||||
The Docker `events` package implements a composable event distribution package
|
||||
for Go.
|
||||
|
||||
Originally created to implement the [notifications in Docker Registry
|
||||
2](https://github.com/docker/distribution/blob/master/docs/notifications.md),
|
||||
we've found the pattern to be useful in other applications. This package is
|
||||
most of the same code with slightly updated interfaces. Much of the internals
|
||||
have been made available.
|
||||
|
||||
## Usage
|
||||
|
||||
The `events` package centers around a `Sink` type. Events are written with
|
||||
calls to `Sink.Write(event Event)`. Sinks can be wired up in various
|
||||
configurations to achieve interesting behavior.
|
||||
|
||||
The canonical example is that employed by the
|
||||
[docker/distribution/notifications](https://godoc.org/github.com/docker/distribution/notifications)
|
||||
package. Let's say we have a type `httpSink` where we'd like to queue
|
||||
notifications. As a rule, it should send a single http request and return an
|
||||
error if it fails:
|
||||
|
||||
```go
|
||||
func (h *httpSink) Write(event Event) error {
|
||||
p, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body := bytes.NewReader(p)
|
||||
resp, err := h.client.Post(h.url, "application/json", body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.Status != 200 {
|
||||
return errors.New("unexpected status")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// implement (*httpSink).Close()
|
||||
```
|
||||
|
||||
With just that, we can start using components from this package. One can call
|
||||
`(*httpSink).Write` to send events as the body of a post request to a
|
||||
configured URL.
|
||||
|
||||
### Retries
|
||||
|
||||
HTTP can be unreliable. The first feature we'd like is to have some retry:
|
||||
|
||||
```go
|
||||
hs := newHTTPSink(/*...*/)
|
||||
retry := NewRetryingSink(hs, NewBreaker(5, time.Second))
|
||||
```
|
||||
|
||||
We now have a sink that will retry events against the `httpSink` until they
|
||||
succeed. The retry will backoff for one second after 5 consecutive failures
|
||||
using the breaker strategy.
|
||||
|
||||
### Queues
|
||||
|
||||
This isn't quite enough. We we want a sink that doesn't block while we are
|
||||
waiting for events to be sent. Let's add a `Queue`:
|
||||
|
||||
```go
|
||||
queue := NewQueue(retry)
|
||||
```
|
||||
|
||||
Now, we have an unbounded queue that will work through all events sent with
|
||||
`(*Queue).Write`. Events can be added asynchronously to the queue without
|
||||
blocking the current execution path. This is ideal for use in an http request.
|
||||
|
||||
### Broadcast
|
||||
|
||||
It usually turns out that you want to send to more than one listener. We can
|
||||
use `Broadcaster` to support this:
|
||||
|
||||
```go
|
||||
var broadcast = NewBroadcaster() // make it available somewhere in your application.
|
||||
broadcast.Add(queue) // add your queue!
|
||||
broadcast.Add(queue2) // and another!
|
||||
```
|
||||
|
||||
With the above, we can now call `broadcast.Write` in our http handlers and have
|
||||
all the events distributed to each queue. Because the events are queued, not
|
||||
listener blocks another.
|
||||
|
||||
### Extending
|
||||
|
||||
For the most part, the above is sufficient for a lot of applications. However,
|
||||
extending the above functionality can be done implementing your own `Sink`. The
|
||||
behavior and semantics of the sink can be completely dependent on the
|
||||
application requirements. The interface is provided below for reference:
|
||||
|
||||
```go
|
||||
type Sink {
|
||||
Write(Event) error
|
||||
Close() error
|
||||
}
|
||||
```
|
||||
|
||||
Application behavior can be controlled by how `Write` behaves. The examples
|
||||
above are designed to queue the message and return as quickly as possible.
|
||||
Other implementations may block until the event is committed to durable
|
||||
storage.
|
158
vendor/src/github.com/docker/go-events/broadcast.go
vendored
Normal file
158
vendor/src/github.com/docker/go-events/broadcast.go
vendored
Normal file
|
@ -0,0 +1,158 @@
|
|||
package events
|
||||
|
||||
import "github.com/Sirupsen/logrus"
|
||||
|
||||
// Broadcaster sends events to multiple, reliable Sinks. The goal of this
|
||||
// component is to dispatch events to configured endpoints. Reliability can be
|
||||
// provided by wrapping incoming sinks.
|
||||
type Broadcaster struct {
|
||||
sinks []Sink
|
||||
events chan Event
|
||||
adds chan configureRequest
|
||||
removes chan configureRequest
|
||||
closed chan chan struct{}
|
||||
}
|
||||
|
||||
// NewBroadcaster appends one or more sinks to the list of sinks. The
|
||||
// broadcaster behavior will be affected by the properties of the sink.
|
||||
// Generally, the sink should accept all messages and deal with reliability on
|
||||
// its own. Use of EventQueue and RetryingSink should be used here.
|
||||
func NewBroadcaster(sinks ...Sink) *Broadcaster {
|
||||
b := Broadcaster{
|
||||
sinks: sinks,
|
||||
events: make(chan Event),
|
||||
adds: make(chan configureRequest),
|
||||
removes: make(chan configureRequest),
|
||||
closed: make(chan chan struct{}),
|
||||
}
|
||||
|
||||
// Start the broadcaster
|
||||
go b.run()
|
||||
|
||||
return &b
|
||||
}
|
||||
|
||||
// Write accepts an event to be dispatched to all sinks. This method will never
|
||||
// fail and should never block (hopefully!). The caller cedes the memory to the
|
||||
// broadcaster and should not modify it after calling write.
|
||||
func (b *Broadcaster) Write(event Event) error {
|
||||
select {
|
||||
case b.events <- event:
|
||||
case <-b.closed:
|
||||
return ErrSinkClosed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add the sink to the broadcaster.
|
||||
//
|
||||
// The provided sink must be comparable with equality. Typically, this just
|
||||
// works with a regular pointer type.
|
||||
func (b *Broadcaster) Add(sink Sink) error {
|
||||
return b.configure(b.adds, sink)
|
||||
}
|
||||
|
||||
// Remove the provided sink.
|
||||
func (b *Broadcaster) Remove(sink Sink) error {
|
||||
return b.configure(b.removes, sink)
|
||||
}
|
||||
|
||||
type configureRequest struct {
|
||||
sink Sink
|
||||
response chan error
|
||||
}
|
||||
|
||||
func (b *Broadcaster) configure(ch chan configureRequest, sink Sink) error {
|
||||
response := make(chan error, 1)
|
||||
|
||||
for {
|
||||
select {
|
||||
case ch <- configureRequest{
|
||||
sink: sink,
|
||||
response: response}:
|
||||
ch = nil
|
||||
case err := <-response:
|
||||
return err
|
||||
case <-b.closed:
|
||||
return ErrSinkClosed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the broadcaster, ensuring that all messages are flushed to the
|
||||
// underlying sink before returning.
|
||||
func (b *Broadcaster) Close() error {
|
||||
select {
|
||||
case <-b.closed:
|
||||
// already closed
|
||||
return ErrSinkClosed
|
||||
default:
|
||||
// do a little chan handoff dance to synchronize closing
|
||||
closed := make(chan struct{})
|
||||
b.closed <- closed
|
||||
close(b.closed)
|
||||
<-closed
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// run is the main broadcast loop, started when the broadcaster is created.
|
||||
// Under normal conditions, it waits for events on the event channel. After
|
||||
// Close is called, this goroutine will exit.
|
||||
func (b *Broadcaster) run() {
|
||||
remove := func(target Sink) {
|
||||
for i, sink := range b.sinks {
|
||||
if sink == target {
|
||||
b.sinks = append(b.sinks[:i], b.sinks[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case event := <-b.events:
|
||||
for _, sink := range b.sinks {
|
||||
if err := sink.Write(event); err != nil {
|
||||
if err == ErrSinkClosed {
|
||||
// remove closed sinks
|
||||
remove(sink)
|
||||
continue
|
||||
}
|
||||
logrus.WithField("event", event).WithField("events.sink", sink).WithError(err).
|
||||
Errorf("broadcaster: dropping event")
|
||||
}
|
||||
}
|
||||
case request := <-b.adds:
|
||||
// while we have to iterate for add/remove, common iteration for
|
||||
// send is faster against slice.
|
||||
|
||||
var found bool
|
||||
for _, sink := range b.sinks {
|
||||
if request.sink == sink {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
b.sinks = append(b.sinks, request.sink)
|
||||
}
|
||||
// b.sinks[request.sink] = struct{}{}
|
||||
request.response <- nil
|
||||
case request := <-b.removes:
|
||||
remove(request.sink)
|
||||
request.response <- nil
|
||||
case closing := <-b.closed:
|
||||
// close all the underlying sinks
|
||||
for _, sink := range b.sinks {
|
||||
if err := sink.Close(); err != nil && err != ErrSinkClosed {
|
||||
logrus.WithField("events.sink", sink).WithError(err).
|
||||
Errorf("broadcaster: closing sink failed")
|
||||
}
|
||||
}
|
||||
closing <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
47
vendor/src/github.com/docker/go-events/channel.go
vendored
Normal file
47
vendor/src/github.com/docker/go-events/channel.go
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
package events
|
||||
|
||||
// Channel provides a sink that can be listened on. The writer and channel
|
||||
// listener must operate in separate goroutines.
|
||||
//
|
||||
// Consumers should listen on Channel.C until Closed is closed.
|
||||
type Channel struct {
|
||||
C chan Event
|
||||
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
// NewChannel returns a channel. If buffer is non-zero, the channel is
|
||||
// unbuffered.
|
||||
func NewChannel(buffer int) *Channel {
|
||||
return &Channel{
|
||||
C: make(chan Event, buffer),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Done returns a channel that will always proceed once the sink is closed.
|
||||
func (ch *Channel) Done() chan struct{} {
|
||||
return ch.closed
|
||||
}
|
||||
|
||||
// Write the event to the channel. Must be called in a separate goroutine from
|
||||
// the listener.
|
||||
func (ch *Channel) Write(event Event) error {
|
||||
select {
|
||||
case ch.C <- event:
|
||||
return nil
|
||||
case <-ch.closed:
|
||||
return ErrSinkClosed
|
||||
}
|
||||
}
|
||||
|
||||
// Close the channel sink.
|
||||
func (ch *Channel) Close() error {
|
||||
select {
|
||||
case <-ch.closed:
|
||||
return ErrSinkClosed
|
||||
default:
|
||||
close(ch.closed)
|
||||
return nil
|
||||
}
|
||||
}
|
10
vendor/src/github.com/docker/go-events/errors.go
vendored
Normal file
10
vendor/src/github.com/docker/go-events/errors.go
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package events
|
||||
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
// ErrSinkClosed is returned if a write is issued to a sink that has been
|
||||
// closed. If encountered, the error should be considered terminal and
|
||||
// retries will not be successful.
|
||||
ErrSinkClosed = fmt.Errorf("events: sink closed")
|
||||
)
|
15
vendor/src/github.com/docker/go-events/event.go
vendored
Normal file
15
vendor/src/github.com/docker/go-events/event.go
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
package events
|
||||
|
||||
// Event marks items that can be sent as events.
|
||||
type Event interface{}
|
||||
|
||||
// Sink accepts and sends events.
|
||||
type Sink interface {
|
||||
// Write an event to the Sink. If no error is returned, the caller will
|
||||
// assume that all events have been committed to the sink. If an error is
|
||||
// received, the caller may retry sending the event.
|
||||
Write(event Event) error
|
||||
|
||||
// Close the sink, possibly waiting for pending events to flush.
|
||||
Close() error
|
||||
}
|
52
vendor/src/github.com/docker/go-events/filter.go
vendored
Normal file
52
vendor/src/github.com/docker/go-events/filter.go
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package events
|
||||
|
||||
// Matcher matches events.
|
||||
type Matcher interface {
|
||||
Match(event Event) bool
|
||||
}
|
||||
|
||||
// MatcherFunc implements matcher with just a function.
|
||||
type MatcherFunc func(event Event) bool
|
||||
|
||||
// Match calls the wrapped function.
|
||||
func (fn MatcherFunc) Match(event Event) bool {
|
||||
return fn(event)
|
||||
}
|
||||
|
||||
// Filter provides an event sink that sends only events that are accepted by a
|
||||
// Matcher. No methods on filter are goroutine safe.
|
||||
type Filter struct {
|
||||
dst Sink
|
||||
matcher Matcher
|
||||
closed bool
|
||||
}
|
||||
|
||||
// NewFilter returns a new filter that will send to events to dst that return
|
||||
// true for Matcher.
|
||||
func NewFilter(dst Sink, matcher Matcher) Sink {
|
||||
return &Filter{dst: dst, matcher: matcher}
|
||||
}
|
||||
|
||||
// Write an event to the filter.
|
||||
func (f *Filter) Write(event Event) error {
|
||||
if f.closed {
|
||||
return ErrSinkClosed
|
||||
}
|
||||
|
||||
if f.matcher.Match(event) {
|
||||
return f.dst.Write(event)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close the filter and allow no more events to pass through.
|
||||
func (f *Filter) Close() error {
|
||||
// TODO(stevvooe): Not all sinks should have Close.
|
||||
if f.closed {
|
||||
return ErrSinkClosed
|
||||
}
|
||||
|
||||
f.closed = true
|
||||
return f.dst.Close()
|
||||
}
|
104
vendor/src/github.com/docker/go-events/queue.go
vendored
Normal file
104
vendor/src/github.com/docker/go-events/queue.go
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Queue accepts all messages into a queue for asynchronous consumption
|
||||
// by a sink. It is unbounded and thread safe but the sink must be reliable or
|
||||
// events will be dropped.
|
||||
type Queue struct {
|
||||
dst Sink
|
||||
events *list.List
|
||||
cond *sync.Cond
|
||||
mu sync.Mutex
|
||||
closed bool
|
||||
}
|
||||
|
||||
// NewQueue returns a queue to the provided Sink dst.
|
||||
func NewQueue(dst Sink) *Queue {
|
||||
eq := Queue{
|
||||
dst: dst,
|
||||
events: list.New(),
|
||||
}
|
||||
|
||||
eq.cond = sync.NewCond(&eq.mu)
|
||||
go eq.run()
|
||||
return &eq
|
||||
}
|
||||
|
||||
// Write accepts the events into the queue, only failing if the queue has
|
||||
// beend closed.
|
||||
func (eq *Queue) Write(event Event) error {
|
||||
eq.mu.Lock()
|
||||
defer eq.mu.Unlock()
|
||||
|
||||
if eq.closed {
|
||||
return ErrSinkClosed
|
||||
}
|
||||
|
||||
eq.events.PushBack(event)
|
||||
eq.cond.Signal() // signal waiters
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close shutsdown the event queue, flushing
|
||||
func (eq *Queue) Close() error {
|
||||
eq.mu.Lock()
|
||||
defer eq.mu.Unlock()
|
||||
|
||||
if eq.closed {
|
||||
return ErrSinkClosed
|
||||
}
|
||||
|
||||
// set closed flag
|
||||
eq.closed = true
|
||||
eq.cond.Signal() // signal flushes queue
|
||||
eq.cond.Wait() // wait for signal from last flush
|
||||
return eq.dst.Close()
|
||||
}
|
||||
|
||||
// run is the main goroutine to flush events to the target sink.
|
||||
func (eq *Queue) run() {
|
||||
for {
|
||||
event := eq.next()
|
||||
|
||||
if event == nil {
|
||||
return // nil block means event queue is closed.
|
||||
}
|
||||
|
||||
if err := eq.dst.Write(event); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"event": event,
|
||||
"sink": eq.dst,
|
||||
}).WithError(err).Warnf("eventqueue: dropped event")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// next encompasses the critical section of the run loop. When the queue is
|
||||
// empty, it will block on the condition. If new data arrives, it will wake
|
||||
// and return a block. When closed, a nil slice will be returned.
|
||||
func (eq *Queue) next() Event {
|
||||
eq.mu.Lock()
|
||||
defer eq.mu.Unlock()
|
||||
|
||||
for eq.events.Len() < 1 {
|
||||
if eq.closed {
|
||||
eq.cond.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
eq.cond.Wait()
|
||||
}
|
||||
|
||||
front := eq.events.Front()
|
||||
block := front.Value.(Event)
|
||||
eq.events.Remove(front)
|
||||
|
||||
return block
|
||||
}
|
168
vendor/src/github.com/docker/go-events/retry.go
vendored
Normal file
168
vendor/src/github.com/docker/go-events/retry.go
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
package events
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// RetryingSink retries the write until success or an ErrSinkClosed is
|
||||
// returned. Underlying sink must have p > 0 of succeeding or the sink will
|
||||
// block. Retry is configured with a RetryStrategy. Concurrent calls to a
|
||||
// retrying sink are serialized through the sink, meaning that if one is
|
||||
// in-flight, another will not proceed.
|
||||
type RetryingSink struct {
|
||||
sink Sink
|
||||
strategy RetryStrategy
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
// NewRetryingSink returns a sink that will retry writes to a sink, backing
|
||||
// off on failure. Parameters threshold and backoff adjust the behavior of the
|
||||
// circuit breaker.
|
||||
func NewRetryingSink(sink Sink, strategy RetryStrategy) *RetryingSink {
|
||||
rs := &RetryingSink{
|
||||
sink: sink,
|
||||
strategy: strategy,
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
|
||||
return rs
|
||||
}
|
||||
|
||||
// Write attempts to flush the events to the downstream sink until it succeeds
|
||||
// or the sink is closed.
|
||||
func (rs *RetryingSink) Write(event Event) error {
|
||||
logger := logrus.WithField("event", event)
|
||||
var timer *time.Timer
|
||||
|
||||
retry:
|
||||
select {
|
||||
case <-rs.closed:
|
||||
return ErrSinkClosed
|
||||
default:
|
||||
}
|
||||
|
||||
if backoff := rs.strategy.Proceed(event); backoff > 0 {
|
||||
if timer == nil {
|
||||
timer = time.NewTimer(backoff)
|
||||
defer timer.Stop()
|
||||
} else {
|
||||
timer.Reset(backoff)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
goto retry
|
||||
case <-rs.closed:
|
||||
return ErrSinkClosed
|
||||
}
|
||||
}
|
||||
|
||||
if err := rs.sink.Write(event); err != nil {
|
||||
if err == ErrSinkClosed {
|
||||
// terminal!
|
||||
return err
|
||||
}
|
||||
|
||||
logger := logger.WithError(err) // shadow!!
|
||||
|
||||
if rs.strategy.Failure(event, err) {
|
||||
logger.Errorf("retryingsink: dropped event")
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Errorf("retryingsink: error writing event, retrying")
|
||||
goto retry
|
||||
}
|
||||
|
||||
rs.strategy.Success(event)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the sink and the underlying sink.
|
||||
func (rs *RetryingSink) Close() error {
|
||||
select {
|
||||
case <-rs.closed:
|
||||
return ErrSinkClosed
|
||||
default:
|
||||
close(rs.closed)
|
||||
return rs.sink.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// RetryStrategy defines a strategy for retrying event sink writes.
|
||||
//
|
||||
// All methods should be goroutine safe.
|
||||
type RetryStrategy interface {
|
||||
// Proceed is called before every event send. If proceed returns a
|
||||
// positive, non-zero integer, the retryer will back off by the provided
|
||||
// duration.
|
||||
//
|
||||
// An event is provided, by may be ignored.
|
||||
Proceed(event Event) time.Duration
|
||||
|
||||
// Failure reports a failure to the strategy. If this method returns true,
|
||||
// the event should be dropped.
|
||||
Failure(event Event, err error) bool
|
||||
|
||||
// Success should be called when an event is sent successfully.
|
||||
Success(event Event)
|
||||
}
|
||||
|
||||
// TODO(stevvooe): We are using circuit breaker here. May want to provide
|
||||
// bounded exponential backoff, as well.
|
||||
|
||||
// Breaker implements a circuit breaker retry strategy.
|
||||
//
|
||||
// The current implementation never drops events.
|
||||
type Breaker struct {
|
||||
threshold int
|
||||
recent int
|
||||
last time.Time
|
||||
backoff time.Duration // time after which we retry after failure.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
var _ RetryStrategy = &Breaker{}
|
||||
|
||||
// NewBreaker returns a breaker that will backoff after the threshold has been
|
||||
// tripped. A Breaker is thread safe and may be shared by many goroutines.
|
||||
func NewBreaker(threshold int, backoff time.Duration) *Breaker {
|
||||
return &Breaker{
|
||||
threshold: threshold,
|
||||
backoff: backoff,
|
||||
}
|
||||
}
|
||||
|
||||
// Proceed checks the failures against the threshold.
|
||||
func (b *Breaker) Proceed(event Event) time.Duration {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
if b.recent < b.threshold {
|
||||
return 0
|
||||
}
|
||||
|
||||
return b.last.Add(b.backoff).Sub(time.Now())
|
||||
}
|
||||
|
||||
// Success resets the breaker.
|
||||
func (b *Breaker) Success(event Event) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
b.recent = 0
|
||||
b.last = time.Time{}
|
||||
}
|
||||
|
||||
// Failure records the failure and latest failure time.
|
||||
func (b *Breaker) Failure(event Event, err error) bool {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
b.recent++
|
||||
b.last = time.Now().UTC()
|
||||
return false // never drop events.
|
||||
}
|
13
vendor/src/github.com/docker/libkv/.travis.yml
vendored
13
vendor/src/github.com/docker/libkv/.travis.yml
vendored
|
@ -1,9 +1,7 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.3
|
||||
# - 1.4
|
||||
# see https://github.com/moovweb/gvm/pull/116 for why Go 1.4 is currently disabled
|
||||
- 1.5.3
|
||||
|
||||
# let us have speedy Docker-based Travis workers
|
||||
sudo: false
|
||||
|
@ -11,19 +9,18 @@ sudo: false
|
|||
before_install:
|
||||
# Symlink below is needed for Travis CI to work correctly on personal forks of libkv
|
||||
- ln -s $HOME/gopath/src/github.com/${TRAVIS_REPO_SLUG///libkv/} $HOME/gopath/src/github.com/docker
|
||||
- go get golang.org/x/tools/cmd/vet
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get github.com/golang/lint/golint
|
||||
- go get github.com/GeertJohan/fgt
|
||||
|
||||
before_script:
|
||||
- script/travis_consul.sh 0.5.2
|
||||
- script/travis_etcd.sh 2.2.0
|
||||
- script/travis_zk.sh 3.4.6
|
||||
- script/travis_consul.sh 0.6.3
|
||||
- script/travis_etcd.sh 2.2.5
|
||||
- script/travis_zk.sh 3.5.1-alpha
|
||||
|
||||
script:
|
||||
- ./consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul -config-file=./config.json 1>/dev/null &
|
||||
- ./consul agent -server -bootstrap -advertise=127.0.0.1 -data-dir /tmp/consul -config-file=./config.json 1>/dev/null &
|
||||
- ./etcd/etcd --listen-client-urls 'http://0.0.0.0:4001' --advertise-client-urls 'http://127.0.0.1:4001' >/dev/null 2>&1 &
|
||||
- ./zk/bin/zkServer.sh start ./zk/conf/zoo.cfg 1> /dev/null
|
||||
- script/validate-gofmt
|
||||
|
|
|
@ -176,7 +176,7 @@
|
|||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014-2015 Docker, Inc.
|
||||
Copyright 2014-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.
|
||||
|
|
46
vendor/src/github.com/docker/libkv/MAINTAINERS
vendored
Normal file
46
vendor/src/github.com/docker/libkv/MAINTAINERS
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Libkv maintainers file
|
||||
#
|
||||
# This file describes who runs the docker/libkv project and how.
|
||||
# This is a living document - if you see something out of date or missing, speak up!
|
||||
#
|
||||
# It is structured to be consumable by both humans and programs.
|
||||
# To extract its contents programmatically, use any TOML-compliant parser.
|
||||
#
|
||||
# This file is compiled into the MAINTAINERS file in docker/opensource.
|
||||
#
|
||||
[Org]
|
||||
[Org."Core maintainers"]
|
||||
people = [
|
||||
"abronan",
|
||||
"aluzzardi",
|
||||
"sanimej",
|
||||
"vieux",
|
||||
]
|
||||
|
||||
[people]
|
||||
|
||||
# A reference list of all people associated with the project.
|
||||
# All other sections should refer to people by their canonical key
|
||||
# in the people section.
|
||||
|
||||
# ADD YOURSELF HERE IN ALPHABETICAL ORDER
|
||||
|
||||
[people.abronan]
|
||||
Name = "Alexandre Beslic"
|
||||
Email = "abronan@docker.com"
|
||||
GitHub = "abronan"
|
||||
|
||||
[people.aluzzardi]
|
||||
Name = "Andrea Luzzardi"
|
||||
Email = "al@docker.com"
|
||||
GitHub = "aluzzardi"
|
||||
|
||||
[people.sanimej]
|
||||
Name = "Santhosh Manohar"
|
||||
Email = "santhosh@docker.com"
|
||||
GitHub = "sanimej"
|
||||
|
||||
[people.vieux]
|
||||
Name = "Victor Vieux"
|
||||
Email = "vieux@docker.com"
|
||||
GitHub = "vieux"
|
9
vendor/src/github.com/docker/libkv/README.md
vendored
9
vendor/src/github.com/docker/libkv/README.md
vendored
|
@ -3,6 +3,7 @@
|
|||
[](https://godoc.org/github.com/docker/libkv)
|
||||
[](https://travis-ci.org/docker/libkv)
|
||||
[](https://coveralls.io/r/docker/libkv)
|
||||
[](https://goreportcard.com/report/github.com/docker/libkv)
|
||||
|
||||
`libkv` provides a `Go` native library to store metadata.
|
||||
|
||||
|
@ -10,7 +11,7 @@ The goal of `libkv` is to abstract common store operations for multiple distribu
|
|||
|
||||
For example, you can use it to store your metadata or for service discovery to register machines and endpoints inside your cluster.
|
||||
|
||||
You can also easily implement a generic *Leader Election* on top of it (see the [swarm/leadership](https://github.com/docker/swarm/tree/master/leadership) package).
|
||||
You can also easily implement a generic *Leader Election* on top of it (see the [docker/leadership](https://github.com/docker/leadership) repository).
|
||||
|
||||
As of now, `libkv` offers support for `Consul`, `Etcd`, `Zookeeper` (**Distributed** store) and `BoltDB` (**Local** store).
|
||||
|
||||
|
@ -30,7 +31,7 @@ You can find examples of usage for `libkv` under in `docs/examples.go`. Optional
|
|||
|
||||
`libkv` supports:
|
||||
- Consul versions >= `0.5.1` because it uses Sessions with `Delete` behavior for the use of `TTLs` (mimics zookeeper's Ephemeral node support), If you don't plan to use `TTLs`: you can use Consul version `0.4.0+`.
|
||||
- Etcd versions >= `2.0` because it uses the new `coreos/etcd/client`, this might change in the future as the support for `APIv3` comes along and adds mor capabilities.
|
||||
- Etcd versions >= `2.0` because it uses the new `coreos/etcd/client`, this might change in the future as the support for `APIv3` comes along and adds more capabilities.
|
||||
- Zookeeper versions >= `3.4.5`. Although this might work with previous version but this remains untested as of now.
|
||||
- Boltdb, which shouldn't be subject to any version dependencies.
|
||||
|
||||
|
@ -83,7 +84,7 @@ Please refer to the `docs/compatibility.md` to see what are the special cases fo
|
|||
|
||||
Other than those special cases, you should expect the same experience for basic operations like `Get`/`Put`, etc.
|
||||
|
||||
Calls like `WatchTree` may return different events (or number of events) depending on the backend (for now, `Etcd` and `Consul` will likely return more events than `Zookeeper` that you should triage properly). Although you should be able to use it successfully to watch on events in an interchangeable way (see the **swarm/leadership** or **swarm/discovery** packages in **docker/swarm**).
|
||||
Calls like `WatchTree` may return different events (or number of events) depending on the backend (for now, `Etcd` and `Consul` will likely return more events than `Zookeeper` that you should triage properly). Although you should be able to use it successfully to watch on events in an interchangeable way (see the **docker/leadership** repository or the **pkg/discovery/kv** package in **docker/docker**).
|
||||
|
||||
## TLS
|
||||
|
||||
|
@ -103,4 +104,4 @@ Want to hack on libkv? [Docker's contributions guidelines](https://github.com/do
|
|||
|
||||
##Copyright and license
|
||||
|
||||
Copyright © 2014-2015 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
|
||||
Copyright © 2014-2016 Docker, Inc. All rights reserved, except as follows. Code is released under the Apache 2.0 license. The README.md file, and files in the "docs" folder are licensed under the Creative Commons Attribution 4.0 International License under the terms and conditions set forth in the file "LICENSE.docs". You may obtain a duplicate copy of the same license, titled CC-BY-SA-4.0, at http://creativecommons.org/licenses/by/4.0/.
|
||||
|
|
2
vendor/src/github.com/docker/libkv/libkv.go
vendored
2
vendor/src/github.com/docker/libkv/libkv.go
vendored
|
@ -25,7 +25,7 @@ var (
|
|||
}()
|
||||
)
|
||||
|
||||
// NewStore creates a an instance of store
|
||||
// NewStore creates an instance of store
|
||||
func NewStore(backend store.Backend, addrs []string, options *store.Config) (store.Store, error) {
|
||||
if init, exists := initializers[backend]; exists {
|
||||
return init(addrs, options)
|
||||
|
|
|
@ -19,8 +19,6 @@ var (
|
|||
// ErrMultipleEndpointsUnsupported is thrown when multiple endpoints specified for
|
||||
// BoltDB. Endpoint has to be a local file path
|
||||
ErrMultipleEndpointsUnsupported = errors.New("boltdb supports one endpoint and should be a file path")
|
||||
// ErrBoltBucketNotFound is thrown when specified BoltBD bucket doesn't exist in the DB
|
||||
ErrBoltBucketNotFound = errors.New("boltdb bucket doesn't exist")
|
||||
// ErrBoltBucketOptionMissing is thrown when boltBcuket config option is missing
|
||||
ErrBoltBucketOptionMissing = errors.New("boltBucket config option missing")
|
||||
)
|
||||
|
@ -141,7 +139,7 @@ func (b *BoltDB) Get(key string) (*store.KVPair, error) {
|
|||
err = db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
|
||||
v := bucket.Get([]byte(key))
|
||||
|
@ -217,7 +215,7 @@ func (b *BoltDB) Delete(key string) error {
|
|||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
err := bucket.Delete([]byte(key))
|
||||
return err
|
||||
|
@ -243,7 +241,7 @@ func (b *BoltDB) Exists(key string) (bool, error) {
|
|||
err = db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
|
||||
val = bucket.Get([]byte(key))
|
||||
|
@ -276,7 +274,7 @@ func (b *BoltDB) List(keyPrefix string) ([]*store.KVPair, error) {
|
|||
err = db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
|
@ -326,7 +324,7 @@ func (b *BoltDB) AtomicDelete(key string, previous *store.KVPair) (bool, error)
|
|||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
|
||||
val = bucket.Get([]byte(key))
|
||||
|
@ -370,7 +368,7 @@ func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, opt
|
|||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
if previous != nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
bucket, err = tx.CreateBucket(b.boltBucket)
|
||||
if err != nil {
|
||||
|
@ -381,7 +379,7 @@ func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, opt
|
|||
// doesn't exist in the DB.
|
||||
val = bucket.Get([]byte(key))
|
||||
if previous == nil && len(val) != 0 {
|
||||
return store.ErrKeyModified
|
||||
return store.ErrKeyExists
|
||||
}
|
||||
if previous != nil {
|
||||
if len(val) == 0 {
|
||||
|
@ -440,7 +438,7 @@ func (b *BoltDB) DeleteTree(keyPrefix string) error {
|
|||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
return store.ErrKeyNotFound
|
||||
}
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
|
|
|
@ -22,6 +22,14 @@ const (
|
|||
// RenewSessionRetryMax is the number of time we should try
|
||||
// to renew the session before giving up and throwing an error
|
||||
RenewSessionRetryMax = 5
|
||||
|
||||
// MaxSessionDestroyAttempts is the maximum times we will try
|
||||
// to explicitely destroy the session attached to a lock after
|
||||
// the connectivity to the store has been lost
|
||||
MaxSessionDestroyAttempts = 5
|
||||
|
||||
// defaultLockTTL is the default ttl for the consul lock
|
||||
defaultLockTTL = 20 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -186,6 +194,7 @@ func (s *Consul) Put(key string, value []byte, opts *store.WriteOptions) error {
|
|||
p := &api.KVPair{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Flags: api.LockFlagValue,
|
||||
}
|
||||
|
||||
if opts != nil && opts.TTL > 0 {
|
||||
|
@ -378,44 +387,99 @@ func (s *Consul) NewLock(key string, options *store.LockOptions) (store.Locker,
|
|||
|
||||
lock := &consulLock{}
|
||||
|
||||
ttl := defaultLockTTL
|
||||
|
||||
if options != nil {
|
||||
// Set optional TTL on Lock
|
||||
if options.TTL != 0 {
|
||||
entry := &api.SessionEntry{
|
||||
Behavior: api.SessionBehaviorRelease, // Release the lock when the session expires
|
||||
TTL: (options.TTL / 2).String(), // Consul multiplies the TTL by 2x
|
||||
LockDelay: 1 * time.Millisecond, // Virtually disable lock delay
|
||||
}
|
||||
|
||||
// Create the key session
|
||||
session, _, err := s.client.Session().Create(entry, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Place the session on lock
|
||||
lockOpts.Session = session
|
||||
|
||||
// Renew the session ttl lock periodically
|
||||
go s.client.Session().RenewPeriodic(entry.TTL, session, nil, options.RenewLock)
|
||||
lock.renewCh = options.RenewLock
|
||||
ttl = options.TTL
|
||||
}
|
||||
|
||||
// Set optional value on Lock
|
||||
if options.Value != nil {
|
||||
lockOpts.Value = options.Value
|
||||
}
|
||||
}
|
||||
|
||||
entry := &api.SessionEntry{
|
||||
Behavior: api.SessionBehaviorRelease, // Release the lock when the session expires
|
||||
TTL: (ttl / 2).String(), // Consul multiplies the TTL by 2x
|
||||
LockDelay: 1 * time.Millisecond, // Virtually disable lock delay
|
||||
}
|
||||
|
||||
// Create the key session
|
||||
session, _, err := s.client.Session().Create(entry, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Place the session and renew chan on lock
|
||||
lockOpts.Session = session
|
||||
lock.renewCh = options.RenewLock
|
||||
|
||||
l, err := s.client.LockOpts(lockOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Renew the session ttl lock periodically
|
||||
s.renewLockSession(entry.TTL, session, options.RenewLock)
|
||||
|
||||
lock.lock = l
|
||||
return lock, nil
|
||||
}
|
||||
|
||||
// renewLockSession is used to renew a session Lock, it takes
|
||||
// a stopRenew chan which is used to explicitely stop the session
|
||||
// renew process. The renew routine never stops until a signal is
|
||||
// sent to this channel. If deleting the session fails because the
|
||||
// connection to the store is lost, it keeps trying to delete the
|
||||
// session periodically until it can contact the store, this ensures
|
||||
// that the lock is not maintained indefinitely which ensures liveness
|
||||
// over safety for the lock when the store becomes unavailable.
|
||||
func (s *Consul) renewLockSession(initialTTL string, id string, stopRenew chan struct{}) {
|
||||
sessionDestroyAttempts := 0
|
||||
ttl, err := time.ParseDuration(initialTTL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(ttl / 2):
|
||||
entry, _, err := s.client.Session().Renew(id, nil)
|
||||
if err != nil {
|
||||
// If an error occurs, continue until the
|
||||
// session gets destroyed explicitely or
|
||||
// the session ttl times out
|
||||
continue
|
||||
}
|
||||
if entry == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle the server updating the TTL
|
||||
ttl, _ = time.ParseDuration(entry.TTL)
|
||||
|
||||
case <-stopRenew:
|
||||
// Attempt a session destroy
|
||||
_, err := s.client.Session().Destroy(id, nil)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if sessionDestroyAttempts >= MaxSessionDestroyAttempts {
|
||||
return
|
||||
}
|
||||
|
||||
// We can't destroy the session because the store
|
||||
// is unavailable, wait for the session renew period
|
||||
sessionDestroyAttempts++
|
||||
time.Sleep(ttl / 2)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Lock attempts to acquire the lock and blocks while
|
||||
// doing so. It returns a channel that is closed if our
|
||||
// lock is lost or if an error occurs
|
||||
|
@ -436,7 +500,7 @@ func (l *consulLock) Unlock() error {
|
|||
// modified in the meantime, throws an error if this is the case
|
||||
func (s *Consul) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
|
||||
|
||||
p := &api.KVPair{Key: s.normalize(key), Value: value}
|
||||
p := &api.KVPair{Key: s.normalize(key), Value: value, Flags: api.LockFlagValue}
|
||||
|
||||
if previous == nil {
|
||||
// Consul interprets ModifyIndex = 0 as new key.
|
||||
|
@ -445,9 +509,14 @@ func (s *Consul) AtomicPut(key string, value []byte, previous *store.KVPair, opt
|
|||
p.ModifyIndex = previous.LastIndex
|
||||
}
|
||||
|
||||
if work, _, err := s.client.KV().CAS(p, nil); err != nil {
|
||||
ok, _, err := s.client.KV().CAS(p, nil)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
} else if !work {
|
||||
}
|
||||
if !ok {
|
||||
if previous == nil {
|
||||
return false, nil, store.ErrKeyExists
|
||||
}
|
||||
return false, nil, store.ErrKeyModified
|
||||
}
|
||||
|
||||
|
@ -466,7 +535,7 @@ func (s *Consul) AtomicDelete(key string, previous *store.KVPair) (bool, error)
|
|||
return false, store.ErrPreviousNotSpecified
|
||||
}
|
||||
|
||||
p := &api.KVPair{Key: s.normalize(key), ModifyIndex: previous.LastIndex}
|
||||
p := &api.KVPair{Key: s.normalize(key), ModifyIndex: previous.LastIndex, Flags: api.LockFlagValue}
|
||||
|
||||
// Extra Get operation to check on the key
|
||||
_, err := s.Get(key)
|
||||
|
|
|
@ -75,6 +75,9 @@ func New(addrs []string, options *store.Config) (store.Store, error) {
|
|||
if options.ConnectionTimeout != 0 {
|
||||
setTimeout(cfg, options.ConnectionTimeout)
|
||||
}
|
||||
if options.Username != "" {
|
||||
setCredentials(cfg, options.Username, options.Password)
|
||||
}
|
||||
}
|
||||
|
||||
c, err := etcd.New(*cfg)
|
||||
|
@ -119,6 +122,12 @@ func setTimeout(cfg *etcd.Config, time time.Duration) {
|
|||
cfg.HeaderTimeoutPerRequest = time
|
||||
}
|
||||
|
||||
// setCredentials sets the username/password credentials for connecting to Etcd
|
||||
func setCredentials(cfg *etcd.Config, username, password string) {
|
||||
cfg.Username = username
|
||||
cfg.Password = password
|
||||
}
|
||||
|
||||
// Normalize the key for usage in Etcd
|
||||
func (s *Etcd) normalize(key string) string {
|
||||
key = store.Normalize(key)
|
||||
|
@ -335,6 +344,10 @@ func (s *Etcd) AtomicPut(key string, value []byte, previous *store.KVPair, opts
|
|||
if etcdError.Code == etcd.ErrorCodeTestFailed {
|
||||
return false, nil, store.ErrKeyModified
|
||||
}
|
||||
// Node exists error (when PrevNoExist)
|
||||
if etcdError.Code == etcd.ErrorCodeNodeExist {
|
||||
return false, nil, store.ErrKeyExists
|
||||
}
|
||||
}
|
||||
return false, nil, err
|
||||
}
|
||||
|
@ -508,15 +521,15 @@ func (l *etcdLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) {
|
|||
// Wait for the key to be available or for
|
||||
// a signal to stop trying to lock the key
|
||||
select {
|
||||
case _ = <-free:
|
||||
case <-free:
|
||||
break
|
||||
case err := <-errorCh:
|
||||
return nil, err
|
||||
case _ = <-stopChan:
|
||||
case <-stopChan:
|
||||
return nil, ErrAbortTryLock
|
||||
}
|
||||
|
||||
// Delete or Expire event occured
|
||||
// Delete or Expire event occurred
|
||||
// Retry
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,8 @@ var (
|
|||
ErrKeyNotFound = errors.New("Key not found in store")
|
||||
// ErrPreviousNotSpecified is thrown when the previous value is not specified for an atomic operation
|
||||
ErrPreviousNotSpecified = errors.New("Previous K/V pair should be provided for the Atomic operation")
|
||||
// ErrKeyExists is thrown when the previous value exists in the case of an AtomicPut
|
||||
ErrKeyExists = errors.New("Previous K/V pair exists, cannot complete Atomic operation")
|
||||
)
|
||||
|
||||
// Config contains the options for a storage client
|
||||
|
@ -44,6 +46,8 @@ type Config struct {
|
|||
ConnectionTimeout time.Duration
|
||||
Bucket string
|
||||
PersistConnection bool
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// ClientTLSConfig contains data for a Client TLS configuration in the form
|
||||
|
|
|
@ -291,8 +291,8 @@ func (s *Zookeeper) DeleteTree(directory string) error {
|
|||
// AtomicPut put a value at "key" if the key has not been
|
||||
// modified in the meantime, throws an error if this is the case
|
||||
func (s *Zookeeper) AtomicPut(key string, value []byte, previous *store.KVPair, _ *store.WriteOptions) (bool, *store.KVPair, error) {
|
||||
|
||||
var lastIndex uint64
|
||||
|
||||
if previous != nil {
|
||||
meta, err := s.client.Set(s.normalize(key), value, int32(previous.LastIndex))
|
||||
if err != nil {
|
||||
|
@ -307,8 +307,9 @@ func (s *Zookeeper) AtomicPut(key string, value []byte, previous *store.KVPair,
|
|||
// Interpret previous == nil as create operation.
|
||||
_, err := s.client.Create(s.normalize(key), value, 0, zk.WorldACL(zk.PermAll))
|
||||
if err != nil {
|
||||
// Zookeeper will complain if the directory doesn't exist.
|
||||
// Directory does not exist
|
||||
if err == zk.ErrNoNode {
|
||||
|
||||
// Create the directory
|
||||
parts := store.SplitKey(strings.TrimSuffix(key, "/"))
|
||||
parts = parts[:len(parts)-1]
|
||||
|
@ -316,11 +317,22 @@ func (s *Zookeeper) AtomicPut(key string, value []byte, previous *store.KVPair,
|
|||
// Failed to create the directory.
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
// Create the node
|
||||
if _, err := s.client.Create(s.normalize(key), value, 0, zk.WorldACL(zk.PermAll)); err != nil {
|
||||
// Node exist error (when previous nil)
|
||||
if err == zk.ErrNodeExists {
|
||||
return false, nil, store.ErrKeyExists
|
||||
}
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
// Node Exists error (when previous nil)
|
||||
if err == zk.ErrNodeExists {
|
||||
return false, nil, store.ErrKeyExists
|
||||
}
|
||||
|
||||
// Unhandled error
|
||||
return false, nil, err
|
||||
}
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
# Changelog
|
||||
|
||||
## 0.8.0-dev.2 (2016-05-07)
|
||||
- Fix an issue which may arise during sandbox cleanup (https://github.com/docker/libnetwork/pull/1157)
|
||||
- Fix cleanup logic in case of ipv6 allocation failure
|
||||
- Don't add /etc/hosts record if container's ip is empty (--net=none)
|
||||
- Fix default gw logic for internal networks
|
||||
- Error when updating IPv6 gateway (https://github.com/docker/libnetwork/issues/1142)
|
||||
- Fixes https://github.com/docker/libnetwork/issues/1113
|
||||
- Fixes https://github.com/docker/libnetwork/issues/1069
|
||||
- Fxies https://github.com/docker/libnetwork/issues/1117
|
||||
- Increase the concurrent query rate-limit count
|
||||
- Changes to build libnetwork in Solaris
|
||||
|
||||
## 0.8.0-dev.1 (2016-04-16)
|
||||
- Fixes docker/docker#16964
|
||||
- Added maximum egress bandwidth qos for Windows
|
||||
|
|
|
@ -5,8 +5,8 @@ dockerargs = --privileged -v $(shell pwd):/go/src/github.com/docker/libnetwork -
|
|||
container_env = -e "INSIDECONTAINER=-incontainer=true"
|
||||
docker = docker run --rm -it ${dockerargs} $$EXTRA_ARGS ${container_env} ${build_image}
|
||||
ciargs = -e CIRCLECI -e "COVERALLS_TOKEN=$$COVERALLS_TOKEN" -e "INSIDECONTAINER=-incontainer=true"
|
||||
cidocker = docker run ${dockerargs} ${ciargs} ${container_env} ${build_image}
|
||||
CROSS_PLATFORMS = linux/amd64 linux/386 linux/arm windows/amd64 windows/386
|
||||
cidocker = docker run ${dockerargs} ${ciargs} $$EXTRA_ARGS ${container_env} ${build_image}
|
||||
CROSS_PLATFORMS = linux/amd64 linux/386 linux/arm windows/amd64
|
||||
|
||||
all: ${build_image}.created build check integration-tests clean
|
||||
|
||||
|
@ -102,4 +102,4 @@ circle-ci-check: ${build_image}.created
|
|||
circle-ci-build: ${build_image}.created
|
||||
@${cidocker} make build-local
|
||||
|
||||
circle-ci: circle-ci-check circle-ci-build integration-tests
|
||||
circle-ci: circle-ci-check circle-ci-cross circle-ci-build integration-tests
|
||||
|
|
|
@ -34,7 +34,7 @@ func main() {
|
|||
|
||||
// Create a network for containers to join.
|
||||
// NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can use.
|
||||
network, err := controller.NewNetwork(networkType, "network1")
|
||||
network, err := controller.NewNetwork(networkType, "network1", "")
|
||||
if err != nil {
|
||||
log.Fatalf("controller.NewNetwork: %s", err)
|
||||
}
|
||||
|
|
369
vendor/src/github.com/docker/libnetwork/agent.go
vendored
Normal file
369
vendor/src/github.com/docker/libnetwork/agent.go
vendored
Normal file
|
@ -0,0 +1,369 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/go-events"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/networkdb"
|
||||
)
|
||||
|
||||
type agent struct {
|
||||
networkDB *networkdb.NetworkDB
|
||||
bindAddr string
|
||||
epTblCancel func()
|
||||
driverCancelFuncs map[string][]func()
|
||||
}
|
||||
|
||||
func getBindAddr(ifaceName string) (string, error) {
|
||||
iface, err := net.InterfaceByName(ifaceName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to find interface %s: %v", ifaceName, err)
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get interface addresses: %v", err)
|
||||
}
|
||||
|
||||
for _, a := range addrs {
|
||||
addr, ok := a.(*net.IPNet)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
addrIP := addr.IP
|
||||
|
||||
if addrIP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
|
||||
return addrIP.String(), nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to get bind address")
|
||||
}
|
||||
|
||||
func resolveAddr(addrOrInterface string) (string, error) {
|
||||
// Try and see if this is a valid IP address
|
||||
if net.ParseIP(addrOrInterface) != nil {
|
||||
return addrOrInterface, nil
|
||||
}
|
||||
|
||||
// If not a valid IP address, it should be a valid interface
|
||||
return getBindAddr(addrOrInterface)
|
||||
}
|
||||
|
||||
func (c *controller) agentInit(bindAddrOrInterface string) error {
|
||||
if !c.cfg.Daemon.IsAgent {
|
||||
return nil
|
||||
}
|
||||
|
||||
bindAddr, err := resolveAddr(bindAddrOrInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostname, _ := os.Hostname()
|
||||
nDB, err := networkdb.New(&networkdb.Config{
|
||||
BindAddr: bindAddr,
|
||||
NodeName: hostname,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch, cancel := nDB.Watch("endpoint_table", "", "")
|
||||
|
||||
c.agent = &agent{
|
||||
networkDB: nDB,
|
||||
bindAddr: bindAddr,
|
||||
epTblCancel: cancel,
|
||||
driverCancelFuncs: make(map[string][]func()),
|
||||
}
|
||||
|
||||
go c.handleTableEvents(ch, c.handleEpTableEvent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) agentJoin(remotes []string) error {
|
||||
if c.agent == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.agent.networkDB.Join(remotes)
|
||||
}
|
||||
|
||||
func (c *controller) agentDriverNotify(d driverapi.Driver) {
|
||||
if c.agent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
d.DiscoverNew(discoverapi.NodeDiscovery, discoverapi.NodeDiscoveryData{
|
||||
Address: c.agent.bindAddr,
|
||||
Self: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *controller) agentClose() {
|
||||
if c.agent == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, cancelFuncs := range c.agent.driverCancelFuncs {
|
||||
for _, cancel := range cancelFuncs {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
c.agent.epTblCancel()
|
||||
|
||||
c.agent.networkDB.Close()
|
||||
}
|
||||
|
||||
func (n *network) isClusterEligible() bool {
|
||||
if n.driverScope() != datastore.GlobalScope {
|
||||
return false
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
if c.agent == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *network) joinCluster() error {
|
||||
if !n.isClusterEligible() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
return c.agent.networkDB.JoinNetwork(n.ID())
|
||||
}
|
||||
|
||||
func (n *network) leaveCluster() error {
|
||||
if !n.isClusterEligible() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
return c.agent.networkDB.LeaveNetwork(n.ID())
|
||||
}
|
||||
|
||||
func (ep *endpoint) addToCluster() error {
|
||||
n := ep.getNetwork()
|
||||
if !n.isClusterEligible() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
if !ep.isAnonymous() && ep.Iface().Address() != nil {
|
||||
if err := c.addServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), ep.Iface().Address().IP); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.agent.networkDB.CreateEntry("endpoint_table", n.ID(), ep.ID(), []byte(fmt.Sprintf("%s,%s,%s,%s", ep.Name(), ep.svcName,
|
||||
ep.svcID, ep.Iface().Address().IP))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, te := range ep.joinInfo.driverTableEntries {
|
||||
if err := c.agent.networkDB.CreateEntry(te.tableName, n.ID(), te.key, te.value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) deleteFromCluster() error {
|
||||
n := ep.getNetwork()
|
||||
if !n.isClusterEligible() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
if !ep.isAnonymous() {
|
||||
if ep.Iface().Address() != nil {
|
||||
if err := c.rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), ep.Iface().Address().IP); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.agent.networkDB.DeleteEntry("endpoint_table", n.ID(), ep.ID()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if ep.joinInfo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, te := range ep.joinInfo.driverTableEntries {
|
||||
if err := c.agent.networkDB.DeleteEntry(te.tableName, n.ID(), te.key); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) addDriverWatches() {
|
||||
if !n.isClusterEligible() {
|
||||
return
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
for _, tableName := range n.driverTables {
|
||||
ch, cancel := c.agent.networkDB.Watch(tableName, n.ID(), "")
|
||||
c.Lock()
|
||||
c.agent.driverCancelFuncs[n.ID()] = append(c.agent.driverCancelFuncs[n.ID()], cancel)
|
||||
c.Unlock()
|
||||
|
||||
go c.handleTableEvents(ch, n.handleDriverTableEvent)
|
||||
d, err := n.driver(false)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not resolve driver %s while walking driver tabl: %v", n.networkType, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.agent.networkDB.WalkTable(tableName, func(nid, key string, value []byte) bool {
|
||||
d.EventNotify(driverapi.Create, n.ID(), tableName, key, value)
|
||||
return false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (n *network) cancelDriverWatches() {
|
||||
if !n.isClusterEligible() {
|
||||
return
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
c.Lock()
|
||||
cancelFuncs := c.agent.driverCancelFuncs[n.ID()]
|
||||
delete(c.agent.driverCancelFuncs, n.ID())
|
||||
c.Unlock()
|
||||
|
||||
for _, cancel := range cancelFuncs {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) handleTableEvents(ch chan events.Event, fn func(events.Event)) {
|
||||
for {
|
||||
select {
|
||||
case ev, ok := <-ch:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
fn(ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *network) handleDriverTableEvent(ev events.Event) {
|
||||
d, err := n.driver(false)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not resolve driver %s while handling driver table event: %v", n.networkType, err)
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
etype driverapi.EventType
|
||||
tname string
|
||||
key string
|
||||
value []byte
|
||||
)
|
||||
|
||||
switch event := ev.(type) {
|
||||
case networkdb.CreateEvent:
|
||||
tname = event.Table
|
||||
key = event.Key
|
||||
value = event.Value
|
||||
etype = driverapi.Create
|
||||
case networkdb.DeleteEvent:
|
||||
tname = event.Table
|
||||
key = event.Key
|
||||
value = event.Value
|
||||
etype = driverapi.Delete
|
||||
case networkdb.UpdateEvent:
|
||||
tname = event.Table
|
||||
key = event.Key
|
||||
value = event.Value
|
||||
etype = driverapi.Delete
|
||||
}
|
||||
|
||||
d.EventNotify(etype, n.ID(), tname, key, value)
|
||||
}
|
||||
|
||||
func (c *controller) handleEpTableEvent(ev events.Event) {
|
||||
var (
|
||||
nid string
|
||||
eid string
|
||||
value string
|
||||
isAdd bool
|
||||
)
|
||||
|
||||
switch event := ev.(type) {
|
||||
case networkdb.CreateEvent:
|
||||
nid = event.NetworkID
|
||||
eid = event.Key
|
||||
value = string(event.Value)
|
||||
isAdd = true
|
||||
case networkdb.DeleteEvent:
|
||||
nid = event.NetworkID
|
||||
eid = event.Key
|
||||
value = string(event.Value)
|
||||
case networkdb.UpdateEvent:
|
||||
logrus.Errorf("Unexpected update service table event = %#v", event)
|
||||
}
|
||||
|
||||
nw, err := c.NetworkByID(nid)
|
||||
if err != nil {
|
||||
logrus.Errorf("Could not find network %s while handling service table event: %v", nid, err)
|
||||
return
|
||||
}
|
||||
n := nw.(*network)
|
||||
|
||||
vals := strings.Split(value, ",")
|
||||
if len(vals) < 4 {
|
||||
logrus.Errorf("Incorrect service table value = %s", value)
|
||||
return
|
||||
}
|
||||
|
||||
name := vals[0]
|
||||
svcName := vals[1]
|
||||
svcID := vals[2]
|
||||
ip := net.ParseIP(vals[3])
|
||||
|
||||
if name == "" || ip == nil {
|
||||
logrus.Errorf("Invalid endpoint name/ip received while handling service table event %s", value)
|
||||
return
|
||||
}
|
||||
|
||||
if isAdd {
|
||||
if err := c.addServiceBinding(svcName, svcID, nid, eid, ip); err != nil {
|
||||
logrus.Errorf("Failed adding service binding for value %s: %v", value, err)
|
||||
return
|
||||
}
|
||||
|
||||
n.addSvcRecords(name, ip, nil, true)
|
||||
} else {
|
||||
if err := c.rmServiceBinding(svcName, svcID, nid, eid, ip); err != nil {
|
||||
logrus.Errorf("Failed adding service binding for value %s: %v", value, err)
|
||||
return
|
||||
}
|
||||
|
||||
n.deleteSvcRecords(name, ip, nil, true)
|
||||
}
|
||||
}
|
|
@ -370,6 +370,8 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool) (uint64
|
|||
|
||||
// checks is needed because to cover the case where the number of bits is not a multiple of blockLen
|
||||
func (h *Handle) validateOrdinal(ordinal uint64) error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
if ordinal >= h.bits {
|
||||
return fmt.Errorf("bit does not belong to the sequence")
|
||||
}
|
||||
|
|
|
@ -75,6 +75,10 @@ func (h *Handle) CopyTo(o datastore.KVObject) error {
|
|||
defer h.Unlock()
|
||||
|
||||
dstH := o.(*Handle)
|
||||
if h == dstH {
|
||||
return nil
|
||||
}
|
||||
dstH.Lock()
|
||||
dstH.bits = h.bits
|
||||
dstH.unselected = h.unselected
|
||||
dstH.head = h.head.getCopy()
|
||||
|
@ -83,6 +87,7 @@ func (h *Handle) CopyTo(o datastore.KVObject) error {
|
|||
dstH.dbIndex = h.dbIndex
|
||||
dstH.dbExists = h.dbExists
|
||||
dstH.store = h.store
|
||||
dstH.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -22,9 +22,12 @@ type Config struct {
|
|||
// DaemonCfg represents libnetwork core configuration
|
||||
type DaemonCfg struct {
|
||||
Debug bool
|
||||
IsAgent bool
|
||||
DataDir string
|
||||
DefaultNetwork string
|
||||
DefaultDriver string
|
||||
Bind string
|
||||
Neighbors []string
|
||||
Labels []string
|
||||
DriverCfg map[string]interface{}
|
||||
}
|
||||
|
@ -81,6 +84,27 @@ func ParseConfigOptions(cfgOptions ...Option) *Config {
|
|||
// to the controller
|
||||
type Option func(c *Config)
|
||||
|
||||
// OptionBind function returns an option setter for setting a bind interface or address
|
||||
func OptionBind(bind string) Option {
|
||||
return func(c *Config) {
|
||||
c.Daemon.Bind = bind
|
||||
}
|
||||
}
|
||||
|
||||
// OptionAgent function returns an option setter for setting agent mode
|
||||
func OptionAgent() Option {
|
||||
return func(c *Config) {
|
||||
c.Daemon.IsAgent = true
|
||||
}
|
||||
}
|
||||
|
||||
// OptionNeighbors function returns an option setter for setting a list of neighbors to join.
|
||||
func OptionNeighbors(neighbors []string) Option {
|
||||
return func(c *Config) {
|
||||
c.Daemon.Neighbors = neighbors
|
||||
}
|
||||
}
|
||||
|
||||
// OptionDefaultNetwork function returns an option setter for a default network
|
||||
func OptionDefaultNetwork(dn string) Option {
|
||||
return func(c *Config) {
|
||||
|
|
|
@ -15,7 +15,7 @@ create network namespaces and allocate interfaces for containers to use.
|
|||
|
||||
// Create a network for containers to join.
|
||||
// NewNetwork accepts Variadic optional arguments that libnetwork and Drivers can make use of
|
||||
network, err := controller.NewNetwork(networkType, "network1")
|
||||
network, err := controller.NewNetwork(networkType, "network1", "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ import (
|
|||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/drvregistry"
|
||||
"github.com/docker/libnetwork/hostdiscovery"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
@ -75,7 +76,7 @@ type NetworkController interface {
|
|||
Config() config.Config
|
||||
|
||||
// Create a new network. The options parameter carries network specific options.
|
||||
NewNetwork(networkType, name string, options ...NetworkOption) (Network, error)
|
||||
NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error)
|
||||
|
||||
// Networks returns the list of Network(s) managed by this controller.
|
||||
Networks() []Network
|
||||
|
@ -119,55 +120,74 @@ type NetworkWalker func(nw Network) bool
|
|||
// When the function returns true, the walk will stop.
|
||||
type SandboxWalker func(sb Sandbox) bool
|
||||
|
||||
type driverData struct {
|
||||
driver driverapi.Driver
|
||||
capability driverapi.Capability
|
||||
}
|
||||
|
||||
type ipamData struct {
|
||||
driver ipamapi.Ipam
|
||||
capability *ipamapi.Capability
|
||||
// default address spaces are provided by ipam driver at registration time
|
||||
defaultLocalAddressSpace, defaultGlobalAddressSpace string
|
||||
}
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
type ipamTable map[string]*ipamData
|
||||
type sandboxTable map[string]*sandbox
|
||||
|
||||
type controller struct {
|
||||
id string
|
||||
drivers driverTable
|
||||
ipamDrivers ipamTable
|
||||
sandboxes sandboxTable
|
||||
cfg *config.Config
|
||||
stores []datastore.DataStore
|
||||
discovery hostdiscovery.HostDiscovery
|
||||
extKeyListener net.Listener
|
||||
watchCh chan *endpoint
|
||||
unWatchCh chan *endpoint
|
||||
svcDb map[string]svcInfo
|
||||
nmap map[string]*netWatch
|
||||
defOsSbox osl.Sandbox
|
||||
sboxOnce sync.Once
|
||||
id string
|
||||
drvRegistry *drvregistry.DrvRegistry
|
||||
sandboxes sandboxTable
|
||||
cfg *config.Config
|
||||
stores []datastore.DataStore
|
||||
discovery hostdiscovery.HostDiscovery
|
||||
extKeyListener net.Listener
|
||||
watchCh chan *endpoint
|
||||
unWatchCh chan *endpoint
|
||||
svcRecords map[string]svcInfo
|
||||
nmap map[string]*netWatch
|
||||
serviceBindings map[string]*service
|
||||
defOsSbox osl.Sandbox
|
||||
sboxOnce sync.Once
|
||||
agent *agent
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type initializer struct {
|
||||
fn drvregistry.InitFunc
|
||||
ntype string
|
||||
}
|
||||
|
||||
// New creates a new instance of network controller.
|
||||
func New(cfgOptions ...config.Option) (NetworkController, error) {
|
||||
c := &controller{
|
||||
id: stringid.GenerateRandomID(),
|
||||
cfg: config.ParseConfigOptions(cfgOptions...),
|
||||
sandboxes: sandboxTable{},
|
||||
drivers: driverTable{},
|
||||
ipamDrivers: ipamTable{},
|
||||
svcDb: make(map[string]svcInfo),
|
||||
id: stringid.GenerateRandomID(),
|
||||
cfg: config.ParseConfigOptions(cfgOptions...),
|
||||
sandboxes: sandboxTable{},
|
||||
svcRecords: make(map[string]svcInfo),
|
||||
serviceBindings: make(map[string]*service),
|
||||
}
|
||||
|
||||
if err := c.agentInit(c.cfg.Daemon.Bind); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.agentJoin(c.cfg.Daemon.Neighbors); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.initStores(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
drvRegistry, err := drvregistry.New(c.getStore(datastore.LocalScope), c.getStore(datastore.GlobalScope), c.RegisterDriver, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, i := range getInitializers() {
|
||||
var dcfg map[string]interface{}
|
||||
|
||||
// External plugins don't need config passed through daemon. They can
|
||||
// bootstrap themselves
|
||||
if i.ntype != "remote" {
|
||||
dcfg = c.makeDriverConfig(i.ntype)
|
||||
}
|
||||
|
||||
if err := drvRegistry.AddDriver(i.ntype, i.fn, dcfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
c.drvRegistry = drvRegistry
|
||||
|
||||
if c.cfg != nil && c.cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil {
|
||||
// Failing to initialize discovery is a bad situation to be in.
|
||||
|
@ -176,15 +196,6 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
|||
}
|
||||
}
|
||||
|
||||
if err := initDrivers(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := initIpams(c, c.getStore(datastore.LocalScope),
|
||||
c.getStore(datastore.GlobalScope)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.sandboxCleanup()
|
||||
c.cleanupLocalEndpoints()
|
||||
c.networkCleanup()
|
||||
|
@ -196,8 +207,67 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func (c *controller) makeDriverConfig(ntype string) map[string]interface{} {
|
||||
if c.cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config := make(map[string]interface{})
|
||||
|
||||
for _, label := range c.cfg.Daemon.Labels {
|
||||
if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) {
|
||||
continue
|
||||
}
|
||||
|
||||
config[netlabel.Key(label)] = netlabel.Value(label)
|
||||
}
|
||||
|
||||
drvCfg, ok := c.cfg.Daemon.DriverCfg[ntype]
|
||||
if ok {
|
||||
for k, v := range drvCfg.(map[string]interface{}) {
|
||||
config[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range c.cfg.Scopes {
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
config[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{
|
||||
Scope: k,
|
||||
Provider: v.Client.Provider,
|
||||
Address: v.Client.Address,
|
||||
Config: v.Client.Config,
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
var procReloadConfig = make(chan (bool), 1)
|
||||
|
||||
func (c *controller) processAgentConfig(cfg *config.Config) (bool, error) {
|
||||
if c.cfg.Daemon.IsAgent == cfg.Daemon.IsAgent {
|
||||
// Agent configuration not changed
|
||||
return false, nil
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.cfg = cfg
|
||||
c.Unlock()
|
||||
|
||||
if err := c.agentInit(c.cfg.Daemon.Bind); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := c.agentJoin(c.cfg.Daemon.Neighbors); err != nil {
|
||||
c.agentClose()
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
||||
procReloadConfig <- true
|
||||
defer func() { <-procReloadConfig }()
|
||||
|
@ -206,6 +276,16 @@ func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
|||
// Refuse the configuration if it alters an existing datastore client configuration.
|
||||
update := false
|
||||
cfg := config.ParseConfigOptions(cfgOptions...)
|
||||
|
||||
isAgentConfig, err := c.processAgentConfig(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isAgentConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
for s := range c.cfg.Scopes {
|
||||
if _, ok := cfg.Scopes[s]; !ok {
|
||||
return types.ForbiddenErrorf("cannot accept new configuration because it removes an existing datastore client")
|
||||
|
@ -228,16 +308,6 @@ func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.cfg = cfg
|
||||
c.Unlock()
|
||||
|
||||
if c.discovery == nil && c.cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil {
|
||||
log.Errorf("Failed to Initialize Discovery after configuration update: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var dsConfig *discoverapi.DatastoreConfigData
|
||||
for scope, sCfg := range cfg.Scopes {
|
||||
if scope == datastore.LocalScope || !sCfg.IsValid() {
|
||||
|
@ -255,17 +325,25 @@ func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
for nm, id := range c.getIpamDrivers() {
|
||||
err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
c.drvRegistry.WalkIPAMs(func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool {
|
||||
err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", nm, err)
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", name, err)
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
for nm, id := range c.getNetDrivers() {
|
||||
err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
||||
err := driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", nm, err)
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", name, err)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
if c.discovery == nil && c.cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil {
|
||||
log.Errorf("Failed to Initialize Discovery after configuration update: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,34 +411,30 @@ func (c *controller) hostLeaveCallback(nodes []net.IP) {
|
|||
}
|
||||
|
||||
func (c *controller) processNodeDiscovery(nodes []net.IP, add bool) {
|
||||
c.Lock()
|
||||
drivers := []*driverData{}
|
||||
for _, d := range c.drivers {
|
||||
drivers = append(drivers, d)
|
||||
}
|
||||
c.Unlock()
|
||||
|
||||
for _, d := range drivers {
|
||||
c.pushNodeDiscovery(d, nodes, add)
|
||||
}
|
||||
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
||||
c.pushNodeDiscovery(driver, capability, nodes, add)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func (c *controller) pushNodeDiscovery(d *driverData, nodes []net.IP, add bool) {
|
||||
func (c *controller) pushNodeDiscovery(d driverapi.Driver, cap driverapi.Capability, nodes []net.IP, add bool) {
|
||||
var self net.IP
|
||||
if c.cfg != nil {
|
||||
addr := strings.Split(c.cfg.Cluster.Address, ":")
|
||||
self = net.ParseIP(addr[0])
|
||||
}
|
||||
if d == nil || d.capability.DataScope != datastore.GlobalScope || nodes == nil {
|
||||
|
||||
if d == nil || cap.DataScope != datastore.GlobalScope || nodes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, node := range nodes {
|
||||
nodeData := discoverapi.NodeDiscoveryData{Address: node.String(), Self: node.Equal(self)}
|
||||
var err error
|
||||
if add {
|
||||
err = d.driver.DiscoverNew(discoverapi.NodeDiscovery, nodeData)
|
||||
err = d.DiscoverNew(discoverapi.NodeDiscovery, nodeData)
|
||||
} else {
|
||||
err = d.driver.DiscoverDelete(discoverapi.NodeDiscovery, nodeData)
|
||||
err = d.DiscoverDelete(discoverapi.NodeDiscovery, nodeData)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debugf("discovery notification error : %v", err)
|
||||
|
@ -378,73 +452,36 @@ func (c *controller) Config() config.Config {
|
|||
}
|
||||
|
||||
func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver, capability driverapi.Capability) error {
|
||||
if !config.IsValidName(networkType) {
|
||||
return ErrInvalidName(networkType)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
if _, ok := c.drivers[networkType]; ok {
|
||||
c.Unlock()
|
||||
return driverapi.ErrActiveRegistration(networkType)
|
||||
}
|
||||
dData := &driverData{driver, capability}
|
||||
c.drivers[networkType] = dData
|
||||
hd := c.discovery
|
||||
c.Unlock()
|
||||
|
||||
if hd != nil {
|
||||
c.pushNodeDiscovery(dData, hd.Fetch(), true)
|
||||
c.pushNodeDiscovery(driver, capability, hd.Fetch(), true)
|
||||
}
|
||||
|
||||
c.agentDriverNotify(driver)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) registerIpamDriver(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
if !config.IsValidName(name) {
|
||||
return ErrInvalidName(name)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
_, ok := c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if ok {
|
||||
return types.ForbiddenErrorf("ipam driver %q already registered", name)
|
||||
}
|
||||
locAS, glbAS, err := driver.GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
|
||||
}
|
||||
c.Lock()
|
||||
c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS, capability: caps}
|
||||
c.Unlock()
|
||||
|
||||
log.Debugf("Registering ipam driver: %q", name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
|
||||
return c.registerIpamDriver(name, driver, &ipamapi.Capability{})
|
||||
}
|
||||
|
||||
func (c *controller) RegisterIpamDriverWithCapabilities(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
return c.registerIpamDriver(name, driver, caps)
|
||||
}
|
||||
|
||||
// NewNetwork creates a new network of the specified network type. The options
|
||||
// are network specific and modeled in a generic way.
|
||||
func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {
|
||||
func (c *controller) NewNetwork(networkType, name string, id string, options ...NetworkOption) (Network, error) {
|
||||
if !config.IsValidName(name) {
|
||||
return nil, ErrInvalidName(name)
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
id = stringid.GenerateRandomID()
|
||||
}
|
||||
|
||||
// Construct the network object
|
||||
network := &network{
|
||||
name: name,
|
||||
networkType: networkType,
|
||||
generic: map[string]interface{}{netlabel.GenericData: make(map[string]string)},
|
||||
ipamType: ipamapi.DefaultIPAM,
|
||||
id: stringid.GenerateRandomID(),
|
||||
id: id,
|
||||
ctrlr: c,
|
||||
persist: true,
|
||||
drvOnce: &sync.Once{},
|
||||
|
@ -468,7 +505,8 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti
|
|||
}
|
||||
}()
|
||||
|
||||
if err = c.addNetwork(network); err != nil {
|
||||
err = c.addNetwork(network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
|
@ -499,6 +537,12 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err = network.joinCluster(); err != nil {
|
||||
log.Errorf("Failed to join network %s into agent cluster: %v", name, err)
|
||||
}
|
||||
|
||||
network.addDriverWatches()
|
||||
|
||||
return network, nil
|
||||
}
|
||||
|
||||
|
@ -509,7 +553,7 @@ func (c *controller) addNetwork(n *network) error {
|
|||
}
|
||||
|
||||
// Create the network
|
||||
if err := d.CreateNetwork(n.id, n.generic, n.getIPData(4), n.getIPData(6)); err != nil {
|
||||
if err := d.CreateNetwork(n.id, n.generic, n, n.getIPData(4), n.getIPData(6)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -745,78 +789,47 @@ func SandboxKeyWalker(out *Sandbox, key string) SandboxWalker {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *controller) loadDriver(networkType string) (*driverData, error) {
|
||||
func (c *controller) loadDriver(networkType string) error {
|
||||
// Plugins pkg performs lazy loading of plugins that acts as remote drivers.
|
||||
// As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available.
|
||||
_, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType)
|
||||
if err != nil {
|
||||
if err == plugins.ErrNotFound {
|
||||
return nil, types.NotFoundErrorf(err.Error())
|
||||
return types.NotFoundErrorf(err.Error())
|
||||
}
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
dd, ok := c.drivers[networkType]
|
||||
if !ok {
|
||||
return nil, ErrInvalidNetworkDriver(networkType)
|
||||
}
|
||||
return dd, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) loadIpamDriver(name string) (*ipamData, error) {
|
||||
func (c *controller) loadIPAMDriver(name string) error {
|
||||
if _, err := plugins.Get(name, ipamapi.PluginEndpointType); err != nil {
|
||||
if err == plugins.ErrNotFound {
|
||||
return nil, types.NotFoundErrorf(err.Error())
|
||||
return types.NotFoundErrorf(err.Error())
|
||||
}
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
id, ok := c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
return nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
|
||||
}
|
||||
return id, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) getIPAM(name string) (id *ipamData, err error) {
|
||||
var ok bool
|
||||
c.Lock()
|
||||
id, ok = c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
id, err = c.loadIpamDriver(name)
|
||||
}
|
||||
return id, err
|
||||
}
|
||||
func (c *controller) getIPAMDriver(name string) (ipamapi.Ipam, *ipamapi.Capability, error) {
|
||||
id, cap := c.drvRegistry.IPAM(name)
|
||||
if id == nil {
|
||||
// Might be a plugin name. Try loading it
|
||||
if err := c.loadIPAMDriver(name); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
func (c *controller) getIpamDriver(name string) (ipamapi.Ipam, error) {
|
||||
id, err := c.getIPAM(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// Now that we resolved the plugin, try again looking up the registry
|
||||
id, cap = c.drvRegistry.IPAM(name)
|
||||
if id == nil {
|
||||
return nil, nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
|
||||
}
|
||||
}
|
||||
return id.driver, nil
|
||||
}
|
||||
|
||||
func (c *controller) getIpamDrivers() ipamTable {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
table := ipamTable{}
|
||||
for i, d := range c.ipamDrivers {
|
||||
table[i] = d
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func (c *controller) getNetDrivers() driverTable {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
table := driverTable{}
|
||||
for i, d := range c.drivers {
|
||||
table[i] = d
|
||||
}
|
||||
return table
|
||||
return id, cap, nil
|
||||
}
|
||||
|
||||
func (c *controller) Stop() {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
)
|
||||
|
||||
type kvMap map[string]KVObject
|
||||
|
@ -42,9 +41,7 @@ func (c *cache) kmap(kvObject KVObject) (kvMap, error) {
|
|||
|
||||
kvList, err := c.ds.store.List(keyPrefix)
|
||||
if err != nil {
|
||||
// In case of BoltDB it may return ErrBoltBucketNotFound when no writes
|
||||
// have ever happened on the db bucket. So check for both err codes
|
||||
if err == store.ErrKeyNotFound || err == boltdb.ErrBoltBucketNotFound {
|
||||
if err == store.ErrKeyNotFound {
|
||||
// If the store doesn't have anything then there is nothing to
|
||||
// populate in the cache. Just bail out.
|
||||
goto out
|
||||
|
|
|
@ -9,10 +9,6 @@ import (
|
|||
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/docker/libkv/store/zookeeper"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -148,13 +144,6 @@ func makeDefaultScopes() map[string]*ScopeCfg {
|
|||
var defaultRootChain = []string{"docker", "network", "v1.0"}
|
||||
var rootChain = defaultRootChain
|
||||
|
||||
func init() {
|
||||
consul.Register()
|
||||
zookeeper.Register()
|
||||
etcd.Register()
|
||||
boltdb.Register()
|
||||
}
|
||||
|
||||
// DefaultScopes returns a map of default scopes and it's config for clients to use.
|
||||
func DefaultScopes(dataDir string) map[string]*ScopeCfg {
|
||||
if dataDir != "" {
|
||||
|
@ -411,6 +400,9 @@ func (ds *datastore) PutObjectAtomic(kvObject KVObject) error {
|
|||
|
||||
_, pair, err = ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil)
|
||||
if err != nil {
|
||||
if err == store.ErrKeyExists {
|
||||
return ErrKeyModified
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -571,6 +563,9 @@ func (ds *datastore) DeleteObjectAtomic(kvObject KVObject) error {
|
|||
}
|
||||
|
||||
if _, err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous); err != nil {
|
||||
if err == store.ErrKeyExists {
|
||||
return ErrKeyModified
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ func (c *controller) createGWNetwork() (Network, error) {
|
|||
bridge.EnableIPMasquerade: strconv.FormatBool(true),
|
||||
}
|
||||
|
||||
n, err := c.NewNetwork("bridge", libnGWNetwork,
|
||||
n, err := c.NewNetwork("bridge", libnGWNetwork, "",
|
||||
NetworkOptionDriverOpts(netOption),
|
||||
NetworkOptionEnableIPv6(false),
|
||||
)
|
||||
|
|
7
vendor/src/github.com/docker/libnetwork/default_gateway_solaris.go
vendored
Normal file
7
vendor/src/github.com/docker/libnetwork/default_gateway_solaris.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
package libnetwork
|
||||
|
||||
import "github.com/docker/libnetwork/types"
|
||||
|
||||
func (c *controller) createGWNetwork() (Network, error) {
|
||||
return nil, types.NotImplementedErrorf("default gateway functionality is not implemented in solaris")
|
||||
}
|
|
@ -13,10 +13,25 @@ const NetworkPluginEndpointType = "NetworkDriver"
|
|||
type Driver interface {
|
||||
discoverapi.Discover
|
||||
|
||||
// CreateNetwork invokes the driver method to create a network passing
|
||||
// the network id and network specific config. The config mechanism will
|
||||
// eventually be replaced with labels which are yet to be introduced.
|
||||
CreateNetwork(nid string, options map[string]interface{}, ipV4Data, ipV6Data []IPAMData) error
|
||||
// NetworkAllocate invokes the driver method to allocate network
|
||||
// specific resources passing network id and network specific config.
|
||||
// It returns a key,value pair of network specific driver allocations
|
||||
// to the caller.
|
||||
NetworkAllocate(nid string, options map[string]string, ipV4Data, ipV6Data []IPAMData) (map[string]string, error)
|
||||
|
||||
// NetworkFree invokes the driver method to free network specific resources
|
||||
// associated with a given network id.
|
||||
NetworkFree(nid string) error
|
||||
|
||||
// CreateNetwork invokes the driver method to create a network
|
||||
// passing the network id and network specific config. The
|
||||
// config mechanism will eventually be replaced with labels
|
||||
// which are yet to be introduced. The driver can return a
|
||||
// list of table names for which it is interested in receiving
|
||||
// notification when a CRUD operation is performed on any
|
||||
// entry in that table. This will be ignored for local scope
|
||||
// drivers.
|
||||
CreateNetwork(nid string, options map[string]interface{}, nInfo NetworkInfo, ipV4Data, ipV6Data []IPAMData) error
|
||||
|
||||
// DeleteNetwork invokes the driver method to delete network passing
|
||||
// the network id.
|
||||
|
@ -50,10 +65,24 @@ type Driver interface {
|
|||
// programming that was done so far
|
||||
RevokeExternalConnectivity(nid, eid string) error
|
||||
|
||||
// EventNotify notifies the driver when a CRUD operation has
|
||||
// happened on a table of its interest as soon as this node
|
||||
// receives such an event in the gossip layer. This method is
|
||||
// only invoked for the global scope driver.
|
||||
EventNotify(event EventType, nid string, tableName string, key string, value []byte)
|
||||
|
||||
// Type returns the the type of this driver, the network type this driver manages
|
||||
Type() string
|
||||
}
|
||||
|
||||
// NetworkInfo provides a go interface for drivers to provide network
|
||||
// specific information to libnetwork.
|
||||
type NetworkInfo interface {
|
||||
// TableEventRegister registers driver interest in a given
|
||||
// table name.
|
||||
TableEventRegister(tableName string) error
|
||||
}
|
||||
|
||||
// InterfaceInfo provides a go interface for drivers to retrive
|
||||
// network information to interface resources.
|
||||
type InterfaceInfo interface {
|
||||
|
@ -102,6 +131,10 @@ type JoinInfo interface {
|
|||
|
||||
// DisableGatewayService tells libnetwork not to provide Default GW for the container
|
||||
DisableGatewayService()
|
||||
|
||||
// AddTableEntry adds a table entry to the gossip layer
|
||||
// passing the table name, key and an opaque value.
|
||||
AddTableEntry(tableName string, key string, value []byte) error
|
||||
}
|
||||
|
||||
// DriverCallback provides a Callback interface for Drivers into LibNetwork
|
||||
|
@ -124,3 +157,15 @@ type IPAMData struct {
|
|||
Gateway *net.IPNet
|
||||
AuxAddresses map[string]*net.IPNet
|
||||
}
|
||||
|
||||
// EventType defines a type for the CRUD event
|
||||
type EventType uint8
|
||||
|
||||
const (
|
||||
// Create event is generated when a table entry is created,
|
||||
Create EventType = 1 + iota
|
||||
// Update event is generated when a table entry is updated.
|
||||
Update
|
||||
// Delete event is generated when a table entry is deleted.
|
||||
Delete
|
||||
)
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
||||
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
|
||||
nullIpam "github.com/docker/libnetwork/ipams/null"
|
||||
remoteIpam "github.com/docker/libnetwork/ipams/remote"
|
||||
)
|
||||
|
||||
type initializer struct {
|
||||
fn func(driverapi.DriverCallback, map[string]interface{}) error
|
||||
ntype string
|
||||
}
|
||||
|
||||
func initDrivers(c *controller) error {
|
||||
for _, i := range getInitializers() {
|
||||
if err := i.fn(c, makeDriverConfig(c, i.ntype)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeDriverConfig(c *controller, ntype string) map[string]interface{} {
|
||||
if c.cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config := make(map[string]interface{})
|
||||
|
||||
for _, label := range c.cfg.Daemon.Labels {
|
||||
if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) {
|
||||
continue
|
||||
}
|
||||
|
||||
config[netlabel.Key(label)] = netlabel.Value(label)
|
||||
}
|
||||
|
||||
drvCfg, ok := c.cfg.Daemon.DriverCfg[ntype]
|
||||
if ok {
|
||||
for k, v := range drvCfg.(map[string]interface{}) {
|
||||
config[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// We don't send datastore configs to external plugins
|
||||
if ntype == "remote" {
|
||||
return config
|
||||
}
|
||||
|
||||
for k, v := range c.cfg.Scopes {
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
config[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{
|
||||
Scope: k,
|
||||
Provider: v.Client.Provider,
|
||||
Address: v.Client.Address,
|
||||
Config: v.Client.Config,
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func initIpams(ic ipamapi.Callback, lDs, gDs interface{}) error {
|
||||
for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
|
||||
builtinIpam.Init,
|
||||
remoteIpam.Init,
|
||||
nullIpam.Init,
|
||||
} {
|
||||
if err := fn(ic, lDs, gDs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -535,8 +535,19 @@ func (d *driver) getNetworks() []*bridgeNetwork {
|
|||
return ls
|
||||
}
|
||||
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
||||
// Create a new network using bridge plugin
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
|
||||
return types.BadRequestErrorf("ipv4 pool is empty")
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
@ -35,7 +34,7 @@ func (d *driver) initStore(option map[string]interface{}) error {
|
|||
|
||||
func (d *driver) populateNetworks() error {
|
||||
kvol, err := d.store.List(datastore.Key(bridgePrefix), &networkConfiguration{})
|
||||
if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound {
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return fmt.Errorf("failed to get bridge network configurations from store: %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,18 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|||
return dc.RegisterDriver(networkType, &driver{}, c)
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -64,6 +65,14 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|||
return dc.RegisterDriver(ipvlanType, d, c)
|
||||
}
|
||||
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
||||
return make(map[string]interface{}, 0), nil
|
||||
}
|
||||
|
@ -89,3 +98,6 @@ func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{})
|
|||
func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
// CreateNetwork the network for the specified driver type
|
||||
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
defer osl.InitOSContext()()
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err != nil {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
@ -60,7 +59,7 @@ func (d *driver) initStore(option map[string]interface{}) error {
|
|||
// populateNetworks is invoked at driver init to recreate persistently stored networks
|
||||
func (d *driver) populateNetworks() error {
|
||||
kvol, err := d.store.List(datastore.Key(ipvlanPrefix), &configuration{})
|
||||
if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound {
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return fmt.Errorf("failed to get ipvlan network configurations from store: %v", err)
|
||||
}
|
||||
// If empty it simply means no ipvlan networks have been created yet
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -66,6 +67,14 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|||
return dc.RegisterDriver(macvlanType, d, c)
|
||||
}
|
||||
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
||||
return make(map[string]interface{}, 0), nil
|
||||
}
|
||||
|
@ -91,3 +100,6 @@ func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{})
|
|||
func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
)
|
||||
|
||||
// CreateNetwork the network for the specified driver type
|
||||
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
defer osl.InitOSContext()()
|
||||
kv, err := kernel.GetKernelVersion()
|
||||
if err != nil {
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
@ -60,7 +59,7 @@ func (d *driver) initStore(option map[string]interface{}) error {
|
|||
// populateNetworks is invoked at driver init to recreate persistently stored networks
|
||||
func (d *driver) populateNetworks() error {
|
||||
kvol, err := d.store.List(datastore.Key(macvlanPrefix), &configuration{})
|
||||
if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound {
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return fmt.Errorf("failed to get macvlan network configurations from store: %v", err)
|
||||
}
|
||||
// If empty it simply means no macvlan networks have been created yet
|
||||
|
|
|
@ -24,7 +24,18 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|||
return dc.RegisterDriver(networkType, &driver{}, c)
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
|
|
|
@ -21,14 +21,18 @@ func chainExists(cname string) bool {
|
|||
}
|
||||
|
||||
func setupGlobalChain() {
|
||||
if err := iptables.RawCombinedOutput("-N", globalChain); err != nil {
|
||||
logrus.Errorf("could not create global overlay chain: %v", err)
|
||||
return
|
||||
// Because of an ungraceful shutdown, chain could already be present
|
||||
if !chainExists(globalChain) {
|
||||
if err := iptables.RawCombinedOutput("-N", globalChain); err != nil {
|
||||
logrus.Errorf("could not create global overlay chain: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := iptables.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil {
|
||||
logrus.Errorf("could not install default return chain in the overlay global chain: %v", err)
|
||||
return
|
||||
if !iptables.Exists(iptables.Filter, globalChain, "-j", "RETURN") {
|
||||
if err := iptables.RawCombinedOutput("-A", globalChain, "-j", "RETURN"); err != nil {
|
||||
logrus.Errorf("could not install default return chain in the overlay global chain: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package overlay
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
|
@ -104,11 +105,55 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
|||
|
||||
d.peerDbAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac,
|
||||
net.ParseIP(d.bindAddress), true)
|
||||
|
||||
if err := jinfo.AddTableEntry(ovPeerTable, eid, []byte(fmt.Sprintf("%s,%s,%s", ep.addr, ep.mac, d.bindAddress))); err != nil {
|
||||
log.Errorf("overlay: Failed adding table entry to joininfo: %v", err)
|
||||
}
|
||||
|
||||
d.pushLocalEndpointEvent("join", nid, eid)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
if tableName != ovPeerTable {
|
||||
log.Errorf("Unexpected table notification for table %s received", tableName)
|
||||
return
|
||||
}
|
||||
|
||||
eid := key
|
||||
values := strings.Split(string(value), ",")
|
||||
if len(values) < 3 {
|
||||
log.Errorf("Invalid value %s received through event notify", string(value))
|
||||
return
|
||||
}
|
||||
|
||||
addr, err := types.ParseCIDR(values[0])
|
||||
if err != nil {
|
||||
log.Errorf("Invalid peer IP %s received in event notify", values[0])
|
||||
return
|
||||
}
|
||||
|
||||
mac, err := net.ParseMAC(values[1])
|
||||
if err != nil {
|
||||
log.Errorf("Invalid mac %s received in event notify", values[1])
|
||||
return
|
||||
}
|
||||
|
||||
vtep := net.ParseIP(values[2])
|
||||
if vtep == nil {
|
||||
log.Errorf("Invalid VTEP %s received in event notify", values[2])
|
||||
return
|
||||
}
|
||||
|
||||
if etype == driverapi.Delete {
|
||||
d.peerDelete(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
|
||||
return
|
||||
}
|
||||
|
||||
d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
|
||||
}
|
||||
|
||||
// Leave method is invoked when a Sandbox detaches from an endpoint.
|
||||
func (d *driver) Leave(nid, eid string) error {
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
@ -13,6 +14,7 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
|
@ -59,7 +61,15 @@ type network struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("invalid network id")
|
||||
}
|
||||
|
@ -81,12 +91,40 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat
|
|||
subnets: []*subnet{},
|
||||
}
|
||||
|
||||
for _, ipd := range ipV4Data {
|
||||
vnis := make([]uint32, 0, len(ipV4Data))
|
||||
if gval, ok := option[netlabel.GenericData]; ok {
|
||||
optMap := gval.(map[string]string)
|
||||
if val, ok := optMap[netlabel.OverlayVxlanIDList]; ok {
|
||||
logrus.Debugf("overlay: Received vxlan IDs: %s", val)
|
||||
vniStrings := strings.Split(val, ",")
|
||||
for _, vniStr := range vniStrings {
|
||||
vni, err := strconv.Atoi(vniStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid vxlan id value %q passed", vniStr)
|
||||
}
|
||||
|
||||
vnis = append(vnis, uint32(vni))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are getting vnis from libnetwork, either we get for
|
||||
// all subnets or none.
|
||||
if len(vnis) != 0 && len(vnis) < len(ipV4Data) {
|
||||
return fmt.Errorf("insufficient vnis(%d) passed to overlay", len(vnis))
|
||||
}
|
||||
|
||||
for i, ipd := range ipV4Data {
|
||||
s := &subnet{
|
||||
subnetIP: ipd.Pool,
|
||||
gwIP: ipd.Gateway,
|
||||
once: &sync.Once{},
|
||||
}
|
||||
|
||||
if len(vnis) != 0 {
|
||||
s.vni = vnis[i]
|
||||
}
|
||||
|
||||
n.subnets = append(n.subnets, s)
|
||||
}
|
||||
|
||||
|
@ -94,8 +132,13 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Dat
|
|||
return fmt.Errorf("failed to update data store for network %v: %v", n.id, err)
|
||||
}
|
||||
|
||||
d.addNetwork(n)
|
||||
if nInfo != nil {
|
||||
if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d.addNetwork(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -244,11 +287,21 @@ func setHostMode() {
|
|||
}
|
||||
|
||||
func (n *network) generateVxlanName(s *subnet) string {
|
||||
return "vx-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + n.id[:5]
|
||||
id := n.id
|
||||
if len(n.id) > 5 {
|
||||
id = n.id[:5]
|
||||
}
|
||||
|
||||
return "vx-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + id
|
||||
}
|
||||
|
||||
func (n *network) generateBridgeName(s *subnet) string {
|
||||
return "ov-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + n.id[:5]
|
||||
id := n.id
|
||||
if len(n.id) > 5 {
|
||||
id = n.id[:5]
|
||||
}
|
||||
|
||||
return "ov-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + id
|
||||
}
|
||||
|
||||
func isOverlap(nw *net.IPNet) bool {
|
||||
|
@ -395,9 +448,10 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) {
|
|||
continue
|
||||
}
|
||||
|
||||
if neigh.IP.To16() != nil {
|
||||
if neigh.IP.To4() == nil {
|
||||
continue
|
||||
}
|
||||
logrus.Debugf("miss notification for dest IP, %v", neigh.IP.String())
|
||||
|
||||
if neigh.State&(netlink.NUD_STALE|netlink.NUD_INCOMPLETE) == 0 {
|
||||
continue
|
||||
|
@ -575,32 +629,38 @@ func (n *network) DataScope() string {
|
|||
}
|
||||
|
||||
func (n *network) writeToStore() error {
|
||||
if n.driver.store == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return n.driver.store.PutObjectAtomic(n)
|
||||
}
|
||||
|
||||
func (n *network) releaseVxlanID() error {
|
||||
if n.driver.store == nil {
|
||||
return fmt.Errorf("no datastore configured. cannot release vxlan id")
|
||||
}
|
||||
|
||||
if len(n.subnets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := n.driver.store.DeleteObjectAtomic(n); err != nil {
|
||||
if err == datastore.ErrKeyModified || err == datastore.ErrKeyNotFound {
|
||||
// In both the above cases we can safely assume that the key has been removed by some other
|
||||
// instance and so simply get out of here
|
||||
return nil
|
||||
}
|
||||
if n.driver.store != nil {
|
||||
if err := n.driver.store.DeleteObjectAtomic(n); err != nil {
|
||||
if err == datastore.ErrKeyModified || err == datastore.ErrKeyNotFound {
|
||||
// In both the above cases we can safely assume that the key has been removed by some other
|
||||
// instance and so simply get out of here
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to delete network to vxlan id map: %v", err)
|
||||
return fmt.Errorf("failed to delete network to vxlan id map: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range n.subnets {
|
||||
n.driver.vxlanIdm.Release(uint64(n.vxlanID(s)))
|
||||
if n.driver.vxlanIdm != nil {
|
||||
n.driver.vxlanIdm.Release(uint64(n.vxlanID(s)))
|
||||
}
|
||||
|
||||
n.setVxlanID(s, 0)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -611,7 +671,7 @@ func (n *network) obtainVxlanID(s *subnet) error {
|
|||
}
|
||||
|
||||
if n.driver.store == nil {
|
||||
return fmt.Errorf("no datastore configured. cannot obtain vxlan id")
|
||||
return fmt.Errorf("no valid vxlan id and no datastore configured, cannot obtain vxlan id")
|
||||
}
|
||||
|
||||
for {
|
||||
|
|
|
@ -88,7 +88,7 @@ func Fini(drv driverapi.Driver) {
|
|||
|
||||
func (d *driver) configure() error {
|
||||
if d.store == nil {
|
||||
return types.NoServiceErrorf("datastore is not available")
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.vxlanIdm == nil {
|
||||
|
@ -147,10 +147,14 @@ func (d *driver) nodeJoin(node string, self bool) {
|
|||
d.Lock()
|
||||
d.bindAddress = node
|
||||
d.Unlock()
|
||||
err := d.serfInit()
|
||||
if err != nil {
|
||||
logrus.Errorf("initializing serf instance failed: %v", err)
|
||||
return
|
||||
|
||||
// If there is no cluster store there is no need to start serf.
|
||||
if d.store != nil {
|
||||
err := d.serfInit()
|
||||
if err != nil {
|
||||
logrus.Errorf("initializing serf instance failed: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
const ovPeerTable = "overlay_peer_table"
|
||||
|
||||
type peerKey struct {
|
||||
peerIP net.IP
|
||||
peerMac net.HardwareAddr
|
||||
|
|
|
@ -83,7 +83,18 @@ func (d *driver) call(methodName string, arg interface{}, retVal maybeError) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, options map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
create := &api.CreateNetworkRequest{
|
||||
NetworkID: id,
|
||||
Options: options,
|
||||
|
|
|
@ -149,8 +149,11 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
|
||||
}
|
||||
|
||||
// Create a new network
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
if _, err := d.getNetwork(id); err == nil {
|
||||
return types.ForbiddenErrorf("network %s exists", id)
|
||||
}
|
||||
|
@ -414,6 +417,10 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
|||
}
|
||||
endpointStruct.Policies = append(endpointStruct.Policies, qosPolicies...)
|
||||
|
||||
if ifInfo.Address() != nil {
|
||||
endpointStruct.IPAddress = ifInfo.Address().IP
|
||||
}
|
||||
|
||||
configurationb, err := json.Marshal(endpointStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -449,8 +456,13 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
|||
n.endpoints[eid] = endpoint
|
||||
n.Unlock()
|
||||
|
||||
ifInfo.SetIPAddress(endpoint.addr)
|
||||
ifInfo.SetMacAddress(endpoint.macAddress)
|
||||
if ifInfo.Address() == nil {
|
||||
ifInfo.SetIPAddress(endpoint.addr)
|
||||
}
|
||||
|
||||
if macAddress == nil {
|
||||
ifInfo.SetMacAddress(endpoint.macAddress)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -560,6 +572,14 @@ func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
|
||||
return nil, types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) NetworkFree(id string) error {
|
||||
return types.NotImplementedErrorf("not implemented")
|
||||
}
|
||||
|
||||
func (d *driver) Type() string {
|
||||
return d.name
|
||||
}
|
||||
|
|
5
vendor/src/github.com/docker/libnetwork/drivers_solaris.go
vendored
Normal file
5
vendor/src/github.com/docker/libnetwork/drivers_solaris.go
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
package libnetwork
|
||||
|
||||
func getInitializers() []initializer {
|
||||
return []initializer{}
|
||||
}
|
241
vendor/src/github.com/docker/libnetwork/drvregistry/drvregistry.go
vendored
Normal file
241
vendor/src/github.com/docker/libnetwork/drvregistry/drvregistry.go
vendored
Normal file
|
@ -0,0 +1,241 @@
|
|||
package drvregistry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
|
||||
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
|
||||
nullIpam "github.com/docker/libnetwork/ipams/null"
|
||||
remoteIpam "github.com/docker/libnetwork/ipams/remote"
|
||||
)
|
||||
|
||||
type driverData struct {
|
||||
driver driverapi.Driver
|
||||
capability driverapi.Capability
|
||||
}
|
||||
|
||||
type ipamData struct {
|
||||
driver ipamapi.Ipam
|
||||
capability *ipamapi.Capability
|
||||
// default address spaces are provided by ipam driver at registration time
|
||||
defaultLocalAddressSpace, defaultGlobalAddressSpace string
|
||||
}
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
type ipamTable map[string]*ipamData
|
||||
|
||||
// DrvRegistry holds the registry of all network drivers and IPAM drivers that it knows about.
|
||||
type DrvRegistry struct {
|
||||
sync.Mutex
|
||||
drivers driverTable
|
||||
ipamDrivers ipamTable
|
||||
dfn DriverNotifyFunc
|
||||
ifn IPAMNotifyFunc
|
||||
}
|
||||
|
||||
// Functors definition
|
||||
|
||||
// InitFunc defines the driver initialization function signature.
|
||||
type InitFunc func(driverapi.DriverCallback, map[string]interface{}) error
|
||||
|
||||
// IPAMWalkFunc defines the IPAM driver table walker function signature.
|
||||
type IPAMWalkFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) bool
|
||||
|
||||
// DriverWalkFunc defines the network driver table walker function signature.
|
||||
type DriverWalkFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) bool
|
||||
|
||||
// IPAMNotifyFunc defines the notify function signature when a new IPAM driver gets registered.
|
||||
type IPAMNotifyFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capability) error
|
||||
|
||||
// DriverNotifyFunc defines the notify function signature when a new network driver gets registered.
|
||||
type DriverNotifyFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) error
|
||||
|
||||
// New retruns a new driver registry handle.
|
||||
func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc) (*DrvRegistry, error) {
|
||||
r := &DrvRegistry{
|
||||
drivers: make(driverTable),
|
||||
ipamDrivers: make(ipamTable),
|
||||
dfn: dfn,
|
||||
ifn: ifn,
|
||||
}
|
||||
|
||||
if err := r.initIPAMs(lDs, gDs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// AddDriver adds a network driver to the registry.
|
||||
func (r *DrvRegistry) AddDriver(ntype string, fn InitFunc, config map[string]interface{}) error {
|
||||
return fn(r, config)
|
||||
}
|
||||
|
||||
// WalkIPAMs walks the IPAM drivers registered in the registry and invokes the passed walk function and each one of them.
|
||||
func (r *DrvRegistry) WalkIPAMs(ifn IPAMWalkFunc) {
|
||||
type ipamVal struct {
|
||||
name string
|
||||
data *ipamData
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
ivl := make([]ipamVal, 0, len(r.ipamDrivers))
|
||||
for k, v := range r.ipamDrivers {
|
||||
ivl = append(ivl, ipamVal{name: k, data: v})
|
||||
}
|
||||
r.Unlock()
|
||||
|
||||
for _, iv := range ivl {
|
||||
if ifn(iv.name, iv.data.driver, iv.data.capability) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WalkDrivers walks the network drivers registered in the registry and invokes the passed walk function and each one of them.
|
||||
func (r *DrvRegistry) WalkDrivers(dfn DriverWalkFunc) {
|
||||
type driverVal struct {
|
||||
name string
|
||||
data *driverData
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
dvl := make([]driverVal, 0, len(r.drivers))
|
||||
for k, v := range r.drivers {
|
||||
dvl = append(dvl, driverVal{name: k, data: v})
|
||||
}
|
||||
r.Unlock()
|
||||
|
||||
for _, dv := range dvl {
|
||||
if dfn(dv.name, dv.data.driver, dv.data.capability) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Driver returns the actual network driver instance and its capability which registered with the passed name.
|
||||
func (r *DrvRegistry) Driver(name string) (driverapi.Driver, *driverapi.Capability) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
d, ok := r.drivers[name]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return d.driver, &d.capability
|
||||
}
|
||||
|
||||
// IPAM returns the actual IPAM driver instance and its capability which registered with the passed name.
|
||||
func (r *DrvRegistry) IPAM(name string) (ipamapi.Ipam, *ipamapi.Capability) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
i, ok := r.ipamDrivers[name]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return i.driver, i.capability
|
||||
}
|
||||
|
||||
// IPAMDefaultAddressSpaces returns the default address space strings for the passed IPAM driver name.
|
||||
func (r *DrvRegistry) IPAMDefaultAddressSpaces(name string) (string, string, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
i, ok := r.ipamDrivers[name]
|
||||
if !ok {
|
||||
return "", "", fmt.Errorf("ipam %s not found", name)
|
||||
}
|
||||
|
||||
return i.defaultLocalAddressSpace, i.defaultGlobalAddressSpace, nil
|
||||
}
|
||||
|
||||
func (r *DrvRegistry) initIPAMs(lDs, gDs interface{}) error {
|
||||
for _, fn := range [](func(ipamapi.Callback, interface{}, interface{}) error){
|
||||
builtinIpam.Init,
|
||||
remoteIpam.Init,
|
||||
nullIpam.Init,
|
||||
} {
|
||||
if err := fn(r, lDs, gDs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterDriver registers the network driver when it gets discovered.
|
||||
func (r *DrvRegistry) RegisterDriver(ntype string, driver driverapi.Driver, capability driverapi.Capability) error {
|
||||
if strings.TrimSpace(ntype) == "" {
|
||||
return fmt.Errorf("network type string cannot be empty")
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
_, ok := r.drivers[ntype]
|
||||
r.Unlock()
|
||||
|
||||
if ok {
|
||||
return driverapi.ErrActiveRegistration(ntype)
|
||||
}
|
||||
|
||||
if r.dfn != nil {
|
||||
if err := r.dfn(ntype, driver, capability); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
dData := &driverData{driver, capability}
|
||||
|
||||
r.Lock()
|
||||
r.drivers[ntype] = dData
|
||||
r.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DrvRegistry) registerIpamDriver(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return fmt.Errorf("ipam driver name string cannot be empty")
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
_, ok := r.ipamDrivers[name]
|
||||
r.Unlock()
|
||||
if ok {
|
||||
return types.ForbiddenErrorf("ipam driver %q already registered", name)
|
||||
}
|
||||
|
||||
locAS, glbAS, err := driver.GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
|
||||
}
|
||||
|
||||
if r.ifn != nil {
|
||||
if err := r.ifn(name, driver, caps); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.Lock()
|
||||
r.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS, capability: caps}
|
||||
r.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterIpamDriver registers the IPAM driver discovered with default capabilities.
|
||||
func (r *DrvRegistry) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
|
||||
return r.registerIpamDriver(name, driver, &ipamapi.Capability{})
|
||||
}
|
||||
|
||||
// RegisterIpamDriverWithCapabilities registers the IPAM driver discovered with specified capabilities.
|
||||
func (r *DrvRegistry) RegisterIpamDriverWithCapabilities(name string, driver ipamapi.Ipam, caps *ipamapi.Capability) error {
|
||||
return r.registerIpamDriver(name, driver, caps)
|
||||
}
|
|
@ -67,6 +67,8 @@ type endpoint struct {
|
|||
ipamOptions map[string]string
|
||||
aliases map[string]string
|
||||
myAliases []string
|
||||
svcID string
|
||||
svcName string
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
sync.Mutex
|
||||
|
@ -89,6 +91,9 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
|
|||
epMap["anonymous"] = ep.anonymous
|
||||
epMap["disableResolution"] = ep.disableResolution
|
||||
epMap["myAliases"] = ep.myAliases
|
||||
epMap["svcName"] = ep.svcName
|
||||
epMap["svcID"] = ep.svcID
|
||||
|
||||
return json.Marshal(epMap)
|
||||
}
|
||||
|
||||
|
@ -172,6 +177,15 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
|
|||
if l, ok := epMap["locator"]; ok {
|
||||
ep.locator = l.(string)
|
||||
}
|
||||
|
||||
if sn, ok := epMap["svcName"]; ok {
|
||||
ep.svcName = sn.(string)
|
||||
}
|
||||
|
||||
if si, ok := epMap["svcID"]; ok {
|
||||
ep.svcID = si.(string)
|
||||
}
|
||||
|
||||
ma, _ := json.Marshal(epMap["myAliases"])
|
||||
var myAliases []string
|
||||
json.Unmarshal(ma, &myAliases)
|
||||
|
@ -196,6 +210,8 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error {
|
|||
dstEp.dbExists = ep.dbExists
|
||||
dstEp.anonymous = ep.anonymous
|
||||
dstEp.disableResolution = ep.disableResolution
|
||||
dstEp.svcName = ep.svcName
|
||||
dstEp.svcID = ep.svcID
|
||||
|
||||
if ep.iface != nil {
|
||||
dstEp.iface = &endpointInterface{}
|
||||
|
@ -413,7 +429,9 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error {
|
|||
}()
|
||||
|
||||
// Watch for service records
|
||||
n.getController().watchSvcRecord(ep)
|
||||
if !n.getController().cfg.Daemon.IsAgent {
|
||||
n.getController().watchSvcRecord(ep)
|
||||
}
|
||||
|
||||
address := ""
|
||||
if ip := ep.getFirstInterfaceAddress(); ip != nil {
|
||||
|
@ -446,6 +464,10 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if e := ep.addToCluster(); e != nil {
|
||||
log.Errorf("Could not update state for endpoint %s into cluster: %v", ep.Name(), e)
|
||||
}
|
||||
|
||||
if sb.needDefaultGW() && sb.getEndpointInGWNetwork() == nil {
|
||||
return sb.setupDefaultGW()
|
||||
}
|
||||
|
@ -632,6 +654,10 @@ func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption)
|
|||
return err
|
||||
}
|
||||
|
||||
if e := ep.deleteFromCluster(); e != nil {
|
||||
log.Errorf("Could not delete state for endpoint %s from cluster: %v", ep.Name(), e)
|
||||
}
|
||||
|
||||
sb.deleteHostsEntries(n.getSvcRecords(ep))
|
||||
if !sb.inDelete && sb.needDefaultGW() && sb.getEndpointInGWNetwork() == nil {
|
||||
return sb.setupDefaultGW()
|
||||
|
@ -730,7 +756,9 @@ func (ep *endpoint) Delete(force bool) error {
|
|||
}()
|
||||
|
||||
// unwatch for service records
|
||||
n.getController().unWatchSvcRecord(ep)
|
||||
if !n.getController().cfg.Daemon.IsAgent {
|
||||
n.getController().unWatchSvcRecord(ep)
|
||||
}
|
||||
|
||||
if err = ep.deleteEndpoint(force); err != nil && !force {
|
||||
return err
|
||||
|
@ -863,6 +891,14 @@ func CreateOptionAlias(name string, alias string) EndpointOption {
|
|||
}
|
||||
}
|
||||
|
||||
// CreateOptionService function returns an option setter for setting service binding configuration
|
||||
func CreateOptionService(name, id string) EndpointOption {
|
||||
return func(ep *endpoint) {
|
||||
ep.svcName = name
|
||||
ep.svcID = id
|
||||
}
|
||||
}
|
||||
|
||||
//CreateOptionMyAlias function returns an option setter for setting endpoint's self alias
|
||||
func CreateOptionMyAlias(alias string) EndpointOption {
|
||||
return func(ep *endpoint) {
|
||||
|
@ -981,7 +1017,7 @@ func (ep *endpoint) releaseAddress() {
|
|||
|
||||
log.Debugf("Releasing addresses for endpoint %s's interface on network %s", ep.Name(), n.Name())
|
||||
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to retrieve ipam driver to release interface address on delete of endpoint %s (%s): %v", ep.Name(), ep.ID(), err)
|
||||
return
|
||||
|
|
|
@ -143,9 +143,16 @@ type endpointJoinInfo struct {
|
|||
gw net.IP
|
||||
gw6 net.IP
|
||||
StaticRoutes []*types.StaticRoute
|
||||
driverTableEntries []*tableEntry
|
||||
disableGatewayService bool
|
||||
}
|
||||
|
||||
type tableEntry struct {
|
||||
tableName string
|
||||
key string
|
||||
value []byte
|
||||
}
|
||||
|
||||
func (ep *endpoint) Info() EndpointInfo {
|
||||
n, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
|
@ -292,6 +299,19 @@ func (ep *endpoint) AddStaticRoute(destination *net.IPNet, routeType int, nextHo
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) AddTableEntry(tableName, key string, value []byte) error {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
|
||||
ep.joinInfo.driverTableEntries = append(ep.joinInfo.driverTableEntries, &tableEntry{
|
||||
tableName: tableName,
|
||||
key: key,
|
||||
value: value,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) Sandbox() Sandbox {
|
||||
cnt, ok := ep.getSandbox()
|
||||
if !ok {
|
||||
|
|
|
@ -3,6 +3,7 @@ package ipam
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
@ -58,9 +59,6 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
|
|||
{localAddressSpace, lcDs},
|
||||
{globalAddressSpace, glDs},
|
||||
} {
|
||||
if aspc.ds == nil {
|
||||
continue
|
||||
}
|
||||
a.initializeAddressSpace(aspc.as, aspc.ds)
|
||||
}
|
||||
|
||||
|
@ -143,15 +141,22 @@ func (a *Allocator) checkConsistency(as string) {
|
|||
}
|
||||
|
||||
func (a *Allocator) initializeAddressSpace(as string, ds datastore.DataStore) error {
|
||||
scope := ""
|
||||
if ds != nil {
|
||||
scope = ds.Scope()
|
||||
}
|
||||
|
||||
a.Lock()
|
||||
if _, ok := a.addrSpaces[as]; ok {
|
||||
a.Unlock()
|
||||
return types.ForbiddenErrorf("tried to add an axisting address space: %s", as)
|
||||
if currAS, ok := a.addrSpaces[as]; ok {
|
||||
if currAS.ds != nil {
|
||||
a.Unlock()
|
||||
return types.ForbiddenErrorf("a datastore is already configured for the address space %s", as)
|
||||
}
|
||||
}
|
||||
a.addrSpaces[as] = &addrSpace{
|
||||
subnets: map[SubnetKey]*PoolData{},
|
||||
id: dsConfigKey + "/" + as,
|
||||
scope: ds.Scope(),
|
||||
scope: scope,
|
||||
ds: ds,
|
||||
alloc: a,
|
||||
}
|
||||
|
@ -313,10 +318,6 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
|||
//log.Debugf("Inserting bitmask (%s, %s)", key.String(), pool.String())
|
||||
|
||||
store := a.getStore(key.AddressSpace)
|
||||
if store == nil {
|
||||
return types.InternalErrorf("could not find store for address space %s while inserting bit mask", key.AddressSpace)
|
||||
}
|
||||
|
||||
ipVer := getAddressVersion(pool.IP)
|
||||
ones, bits := pool.Mask.Size()
|
||||
numAddresses := uint64(1 << uint(bits-ones))
|
||||
|
@ -401,13 +402,6 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
|
|||
}
|
||||
|
||||
if !aSpace.contains(as, nw) {
|
||||
if as == localAddressSpace {
|
||||
// Check if nw overlap with system routes, name servers
|
||||
if _, err := ipamutils.FindAvailableNetwork([]*net.IPNet{nw}); err == nil {
|
||||
return nw, nil
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nw, nil
|
||||
}
|
||||
}
|
||||
|
@ -563,13 +557,18 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres
|
|||
func (a *Allocator) DumpDatabase() string {
|
||||
a.Lock()
|
||||
aspaces := make(map[string]*addrSpace, len(a.addrSpaces))
|
||||
orderedAS := make([]string, 0, len(a.addrSpaces))
|
||||
for as, aSpace := range a.addrSpaces {
|
||||
orderedAS = append(orderedAS, as)
|
||||
aspaces[as] = aSpace
|
||||
}
|
||||
a.Unlock()
|
||||
|
||||
sort.Strings(orderedAS)
|
||||
|
||||
var s string
|
||||
for as, aSpace := range aspaces {
|
||||
for _, as := range orderedAS {
|
||||
aSpace := aspaces[as]
|
||||
s = fmt.Sprintf("\n\n%s Config", as)
|
||||
aSpace.Lock()
|
||||
for k, config := range aSpace.subnets {
|
||||
|
|
|
@ -82,8 +82,10 @@ func (a *Allocator) getStore(as string) datastore.DataStore {
|
|||
|
||||
func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
|
||||
store := a.getStore(as)
|
||||
|
||||
// IPAM may not have a valid store. In such cases it is just in-memory state.
|
||||
if store == nil {
|
||||
return nil, types.InternalErrorf("store for address space %s not found", as)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a}
|
||||
|
@ -100,8 +102,10 @@ func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
|
|||
|
||||
func (a *Allocator) writeToStore(aSpace *addrSpace) error {
|
||||
store := aSpace.store()
|
||||
|
||||
// IPAM may not have a valid store. In such cases it is just in-memory state.
|
||||
if store == nil {
|
||||
return types.InternalErrorf("invalid store while trying to write %s address space", aSpace.DataScope())
|
||||
return nil
|
||||
}
|
||||
|
||||
err := store.PutObjectAtomic(aSpace)
|
||||
|
@ -114,8 +118,10 @@ func (a *Allocator) writeToStore(aSpace *addrSpace) error {
|
|||
|
||||
func (a *Allocator) deleteFromStore(aSpace *addrSpace) error {
|
||||
store := aSpace.store()
|
||||
|
||||
// IPAM may not have a valid store. In such cases it is just in-memory state.
|
||||
if store == nil {
|
||||
return types.InternalErrorf("invalid store while trying to delete %s address space", aSpace.DataScope())
|
||||
return nil
|
||||
}
|
||||
|
||||
return store.DeleteObjectAtomic(aSpace)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// +build linux freebsd
|
||||
// +build linux freebsd solaris darwin
|
||||
|
||||
package builtin
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
// Package ipamutils provides utililty functions for ipam management
|
||||
package ipamutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// ElectInterfaceAddresses looks for an interface on the OS with the specified name
|
||||
// and returns its IPv4 and IPv6 addresses in CIDR form. If the interface does not exist,
|
||||
// it chooses from a predifined list the first IPv4 address which does not conflict
|
||||
// with other interfaces on the system.
|
||||
func ElectInterfaceAddresses(name string) (*net.IPNet, []*net.IPNet, error) {
|
||||
var (
|
||||
v4Net *net.IPNet
|
||||
v6Nets []*net.IPNet
|
||||
err error
|
||||
)
|
||||
|
||||
InitNetworks()
|
||||
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
link, _ := netlink.LinkByName(name)
|
||||
if link != nil {
|
||||
v4addr, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
v6addr, err := netlink.AddrList(link, netlink.FAMILY_V6)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(v4addr) > 0 {
|
||||
v4Net = v4addr[0].IPNet
|
||||
}
|
||||
for _, nlAddr := range v6addr {
|
||||
v6Nets = append(v6Nets, nlAddr.IPNet)
|
||||
}
|
||||
}
|
||||
|
||||
if link == nil || v4Net == nil {
|
||||
// Choose from predifined broad networks
|
||||
v4Net, err = FindAvailableNetwork(PredefinedBroadNetworks)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return v4Net, v6Nets, nil
|
||||
}
|
||||
|
||||
// FindAvailableNetwork returns a network from the passed list which does not
|
||||
// overlap with existing interfaces in the system
|
||||
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
|
||||
// We don't check for an error here, because we don't really care if we
|
||||
// can't read /etc/resolv.conf. So instead we skip the append if resolvConf
|
||||
// is nil. It either doesn't exist, or we can't read it for some reason.
|
||||
var nameservers []string
|
||||
if rc, err := resolvconf.Get(); err == nil {
|
||||
nameservers = resolvconf.GetNameserversAsCIDR(rc.Content)
|
||||
}
|
||||
for _, nw := range list {
|
||||
if err := netutils.CheckNameserverOverlaps(nameservers, nw); err == nil {
|
||||
if err := netutils.CheckRouteOverlaps(nw); err == nil {
|
||||
return nw, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no available network")
|
||||
}
|
|
@ -39,6 +39,9 @@ const (
|
|||
// OverlayNeighborIP constant represents overlay driver neighbor IP
|
||||
OverlayNeighborIP = DriverPrefix + ".overlay.neighbor_ip"
|
||||
|
||||
// OverlayVxlanIDList constant represents a list of VXLAN Ids as csv
|
||||
OverlayVxlanIDList = DriverPrefix + ".overlay.vxlanid_list"
|
||||
|
||||
// Gateway represents the gateway for the network
|
||||
Gateway = Prefix + ".gateway"
|
||||
|
||||
|
|
|
@ -14,13 +14,6 @@ import (
|
|||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// constants for the IP address type
|
||||
const (
|
||||
IP = iota // IPv4 and IPv6
|
||||
IPv4
|
||||
IPv6
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNetworkOverlapsWithNameservers preformatted error
|
||||
ErrNetworkOverlapsWithNameservers = errors.New("requested network overlaps with nameserver")
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// Package ipamutils provides utililty functions for ipam management
|
||||
package ipamutils
|
||||
package netutils
|
||||
|
||||
import (
|
||||
"net"
|
|
@ -4,9 +4,13 @@
|
|||
package netutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork/ipamutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
@ -48,3 +52,66 @@ func GenerateIfaceName(prefix string, len int) (string, error) {
|
|||
}
|
||||
return "", types.InternalErrorf("could not generate interface name")
|
||||
}
|
||||
|
||||
// ElectInterfaceAddresses looks for an interface on the OS with the
|
||||
// specified name and returns its IPv4 and IPv6 addresses in CIDR
|
||||
// form. If the interface does not exist, it chooses from a predifined
|
||||
// list the first IPv4 address which does not conflict with other
|
||||
// interfaces on the system.
|
||||
func ElectInterfaceAddresses(name string) (*net.IPNet, []*net.IPNet, error) {
|
||||
var (
|
||||
v4Net *net.IPNet
|
||||
v6Nets []*net.IPNet
|
||||
err error
|
||||
)
|
||||
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
link, _ := netlink.LinkByName(name)
|
||||
if link != nil {
|
||||
v4addr, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
v6addr, err := netlink.AddrList(link, netlink.FAMILY_V6)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(v4addr) > 0 {
|
||||
v4Net = v4addr[0].IPNet
|
||||
}
|
||||
for _, nlAddr := range v6addr {
|
||||
v6Nets = append(v6Nets, nlAddr.IPNet)
|
||||
}
|
||||
}
|
||||
|
||||
if link == nil || v4Net == nil {
|
||||
// Choose from predifined broad networks
|
||||
v4Net, err = FindAvailableNetwork(ipamutils.PredefinedBroadNetworks)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return v4Net, v6Nets, nil
|
||||
}
|
||||
|
||||
// FindAvailableNetwork returns a network from the passed list which does not
|
||||
// overlap with existing interfaces in the system
|
||||
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
|
||||
// We don't check for an error here, because we don't really care if we
|
||||
// can't read /etc/resolv.conf. So instead we skip the append if resolvConf
|
||||
// is nil. It either doesn't exist, or we can't read it for some reason.
|
||||
var nameservers []string
|
||||
if rc, err := resolvconf.Get(); err == nil {
|
||||
nameservers = resolvconf.GetNameserversAsCIDR(rc.Content)
|
||||
}
|
||||
for _, nw := range list {
|
||||
if err := CheckNameserverOverlaps(nameservers, nw); err == nil {
|
||||
if err := CheckRouteOverlaps(nw); err == nil {
|
||||
return nw, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no available network")
|
||||
}
|
||||
|
|
32
vendor/src/github.com/docker/libnetwork/netutils/utils_solaris.go
vendored
Normal file
32
vendor/src/github.com/docker/libnetwork/netutils/utils_solaris.go
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
package netutils
|
||||
|
||||
// Solaris: TODO
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/ipamutils"
|
||||
)
|
||||
|
||||
// ElectInterfaceAddresses looks for an interface on the OS with the specified name
|
||||
// and returns its IPv4 and IPv6 addresses in CIDR form. If the interface does not exist,
|
||||
// it chooses from a predifined list the first IPv4 address which does not conflict
|
||||
// with other interfaces on the system.
|
||||
func ElectInterfaceAddresses(name string) (*net.IPNet, []*net.IPNet, error) {
|
||||
var (
|
||||
v4Net *net.IPNet
|
||||
err error
|
||||
)
|
||||
|
||||
v4Net, err = FindAvailableNetwork(ipamutils.PredefinedBroadNetworks)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return v4Net, nil, nil
|
||||
}
|
||||
|
||||
// FindAvailableNetwork returns a network from the passed list which does not
|
||||
// overlap with existing interfaces in the system
|
||||
func FindAvailableNetwork(list []*net.IPNet) (*net.IPNet, error) {
|
||||
return list[0], nil
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
// Package ipamutils provides utililty functions for ipam management
|
||||
package ipamutils
|
||||
package netutils
|
||||
|
||||
import (
|
||||
"net"
|
169
vendor/src/github.com/docker/libnetwork/network.go
vendored
169
vendor/src/github.com/docker/libnetwork/network.go
vendored
|
@ -171,6 +171,7 @@ type network struct {
|
|||
drvOnce *sync.Once
|
||||
internal bool
|
||||
inDelete bool
|
||||
driverTables []string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -620,49 +621,62 @@ func (n *network) processOptions(options ...NetworkOption) {
|
|||
}
|
||||
}
|
||||
|
||||
func (n *network) driverScope() string {
|
||||
func (n *network) resolveDriver(name string, load bool) (driverapi.Driver, *driverapi.Capability, error) {
|
||||
c := n.getController()
|
||||
|
||||
c.Lock()
|
||||
// Check if a driver for the specified network type is available
|
||||
dd, ok := c.drivers[n.networkType]
|
||||
c.Unlock()
|
||||
d, cap := c.drvRegistry.Driver(name)
|
||||
if d == nil {
|
||||
if load {
|
||||
var err error
|
||||
err = c.loadDriver(name)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !ok {
|
||||
var err error
|
||||
dd, err = c.loadDriver(n.networkType)
|
||||
if err != nil {
|
||||
// If driver could not be resolved simply return an empty string
|
||||
return ""
|
||||
d, cap = c.drvRegistry.Driver(name)
|
||||
if d == nil {
|
||||
return nil, nil, fmt.Errorf("could not resolve driver %s in registry", name)
|
||||
}
|
||||
} else {
|
||||
// don't fail if driver loading is not required
|
||||
return nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return dd.capability.DataScope
|
||||
return d, cap, nil
|
||||
}
|
||||
|
||||
func (n *network) driverScope() string {
|
||||
_, cap, err := n.resolveDriver(n.networkType, true)
|
||||
if err != nil {
|
||||
// If driver could not be resolved simply return an empty string
|
||||
return ""
|
||||
}
|
||||
|
||||
return cap.DataScope
|
||||
}
|
||||
|
||||
func (n *network) driver(load bool) (driverapi.Driver, error) {
|
||||
c := n.getController()
|
||||
|
||||
c.Lock()
|
||||
// Check if a driver for the specified network type is available
|
||||
dd, ok := c.drivers[n.networkType]
|
||||
c.Unlock()
|
||||
|
||||
if !ok && load {
|
||||
var err error
|
||||
dd, err = c.loadDriver(n.networkType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if !ok {
|
||||
// don't fail if driver loading is not required
|
||||
return nil, nil
|
||||
d, cap, err := n.resolveDriver(n.networkType, load)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := n.getController()
|
||||
n.Lock()
|
||||
n.scope = dd.capability.DataScope
|
||||
// If load is not required, driver, cap and err may all be nil
|
||||
if cap != nil {
|
||||
n.scope = cap.DataScope
|
||||
}
|
||||
if c.cfg.Daemon.IsAgent {
|
||||
// If we are running in agent mode then all networks
|
||||
// in libnetwork are local scope regardless of the
|
||||
// backing driver.
|
||||
n.scope = datastore.LocalScope
|
||||
}
|
||||
n.Unlock()
|
||||
return dd.driver, nil
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func (n *network) Delete() error {
|
||||
|
@ -717,6 +731,12 @@ func (n *network) delete(force bool) error {
|
|||
return fmt.Errorf("error deleting network from store: %v", err)
|
||||
}
|
||||
|
||||
n.cancelDriverWatches()
|
||||
|
||||
if err = n.leaveCluster(); err != nil {
|
||||
log.Errorf("Failed leaving network %s from the agent cluster: %v", n.Name(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -786,12 +806,12 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|||
}
|
||||
}
|
||||
|
||||
ipam, err := n.getController().getIPAM(n.ipamType)
|
||||
ipam, cap, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ipam.capability.RequiresMACAddress {
|
||||
if cap.RequiresMACAddress {
|
||||
if ep.iface.mac == nil {
|
||||
ep.iface.mac = netutils.GenerateRandomMAC()
|
||||
}
|
||||
|
@ -801,7 +821,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|||
ep.ipamOptions[netlabel.MacAddress] = ep.iface.mac.String()
|
||||
}
|
||||
|
||||
if err = ep.assignAddress(ipam.driver, true, n.enableIPv6 && !n.postIPv6); err != nil {
|
||||
if err = ep.assignAddress(ipam, true, n.enableIPv6 && !n.postIPv6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
|
@ -821,7 +841,7 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|||
}
|
||||
}()
|
||||
|
||||
if err = ep.assignAddress(ipam.driver, false, n.enableIPv6 && n.postIPv6); err != nil {
|
||||
if err = ep.assignAddress(ipam, false, n.enableIPv6 && n.postIPv6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -985,14 +1005,14 @@ func (n *network) addSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMapUp
|
|||
c := n.getController()
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
sr, ok := c.svcDb[n.ID()]
|
||||
sr, ok := c.svcRecords[n.ID()]
|
||||
if !ok {
|
||||
sr = svcInfo{
|
||||
svcMap: make(map[string][]net.IP),
|
||||
svcIPv6Map: make(map[string][]net.IP),
|
||||
ipMap: make(map[string]string),
|
||||
}
|
||||
c.svcDb[n.ID()] = sr
|
||||
c.svcRecords[n.ID()] = sr
|
||||
}
|
||||
|
||||
if ipMapUpdate {
|
||||
|
@ -1012,7 +1032,7 @@ func (n *network) deleteSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMa
|
|||
c := n.getController()
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
sr, ok := c.svcDb[n.ID()]
|
||||
sr, ok := c.svcRecords[n.ID()]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
@ -1037,7 +1057,7 @@ func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
|
|||
defer n.Unlock()
|
||||
|
||||
var recs []etchosts.Record
|
||||
sr, _ := n.ctrlr.svcDb[n.id]
|
||||
sr, _ := n.ctrlr.svcRecords[n.id]
|
||||
|
||||
for h, ip := range sr.svcMap {
|
||||
if ep != nil && strings.Split(h, ".")[0] == ep.Name() {
|
||||
|
@ -1065,7 +1085,7 @@ func (n *network) ipamAllocate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1091,7 +1111,53 @@ func (n *network) ipamAllocate() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
return n.ipamAllocateVersion(6, ipam)
|
||||
err = n.ipamAllocateVersion(6, ipam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) requestPoolHelper(ipam ipamapi.Ipam, addressSpace, preferredPool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
||||
for {
|
||||
poolID, pool, meta, err := ipam.RequestPool(addressSpace, preferredPool, subPool, options, v6)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
// If the network belongs to global scope or the pool was
|
||||
// explicitely chosen or it is invalid, do not perform the overlap check.
|
||||
if n.Scope() == datastore.GlobalScope || preferredPool != "" || !types.IsIPNetValid(pool) {
|
||||
return poolID, pool, meta, nil
|
||||
}
|
||||
|
||||
// Check for overlap and if none found, we have found the right pool.
|
||||
if _, err := netutils.FindAvailableNetwork([]*net.IPNet{pool}); err == nil {
|
||||
return poolID, pool, meta, nil
|
||||
}
|
||||
|
||||
// Pool obtained in this iteration is
|
||||
// overlapping. Hold onto the pool and don't release
|
||||
// it yet, because we don't want ipam to give us back
|
||||
// the same pool over again. But make sure we still do
|
||||
// a deferred release when we have either obtained a
|
||||
// non-overlapping pool or ran out of pre-defined
|
||||
// pools.
|
||||
defer func() {
|
||||
if err := ipam.ReleasePool(poolID); err != nil {
|
||||
log.Warnf("Failed to release overlapping pool %s while returning from pool request helper for network %s", pool, n.Name())
|
||||
}
|
||||
}()
|
||||
|
||||
// If this is a preferred pool request and the network
|
||||
// is local scope and there is a overlap, we fail the
|
||||
// network creation right here. The pool will be
|
||||
// released in the defer.
|
||||
if preferredPool != "" {
|
||||
return "", nil, nil, fmt.Errorf("requested subnet %s overlaps in the host", preferredPool)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||
|
@ -1130,7 +1196,7 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
|
|||
d := &IpamInfo{}
|
||||
(*infoList)[i] = d
|
||||
|
||||
d.PoolID, d.Pool, d.Meta, err = ipam.RequestPool(n.addrSpace, cfg.PreferredPool, cfg.SubPool, n.ipamOptions, ipVer == 6)
|
||||
d.PoolID, d.Pool, d.Meta, err = n.requestPoolHelper(ipam, n.addrSpace, cfg.PreferredPool, cfg.SubPool, n.ipamOptions, ipVer == 6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1189,7 +1255,7 @@ func (n *network) ipamRelease() {
|
|||
if n.Type() == "host" || n.Type() == "null" {
|
||||
return
|
||||
}
|
||||
ipam, err := n.getController().getIpamDriver(n.ipamType)
|
||||
ipam, _, err := n.getController().getIPAMDriver(n.ipamType)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err)
|
||||
return
|
||||
|
@ -1279,17 +1345,14 @@ func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
|
|||
}
|
||||
|
||||
func (n *network) deriveAddressSpace() (string, error) {
|
||||
c := n.getController()
|
||||
c.Lock()
|
||||
ipd, ok := c.ipamDrivers[n.ipamType]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
return "", types.NotFoundErrorf("could not find ipam driver %s to get default address space", n.ipamType)
|
||||
local, global, err := n.getController().drvRegistry.IPAMDefaultAddressSpaces(n.ipamType)
|
||||
if err != nil {
|
||||
return "", types.NotFoundErrorf("failed to get default address space: %v", err)
|
||||
}
|
||||
if n.DataScope() == datastore.GlobalScope {
|
||||
return ipd.defaultGlobalAddressSpace, nil
|
||||
return global, nil
|
||||
}
|
||||
return ipd.defaultLocalAddressSpace, nil
|
||||
return local, nil
|
||||
}
|
||||
|
||||
func (n *network) Info() NetworkInfo {
|
||||
|
@ -1382,3 +1445,11 @@ func (n *network) Labels() map[string]string {
|
|||
|
||||
return lbls
|
||||
}
|
||||
|
||||
func (n *network) TableEventRegister(tableName string) error {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
n.driverTables = append(n.driverTables, tableName)
|
||||
return nil
|
||||
}
|
||||
|
|
127
vendor/src/github.com/docker/libnetwork/networkdb/broadcast.go
vendored
Normal file
127
vendor/src/github.com/docker/libnetwork/networkdb/broadcast.go
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package networkdb
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
type networkEventType uint8
|
||||
|
||||
const (
|
||||
networkJoin networkEventType = 1 + iota
|
||||
networkLeave
|
||||
)
|
||||
|
||||
type networkEventData struct {
|
||||
Event networkEventType
|
||||
LTime serf.LamportTime
|
||||
NodeName string
|
||||
NetworkID string
|
||||
}
|
||||
|
||||
type networkEventMessage struct {
|
||||
id string
|
||||
node string
|
||||
msg []byte
|
||||
}
|
||||
|
||||
func (m *networkEventMessage) Invalidates(other memberlist.Broadcast) bool {
|
||||
otherm := other.(*networkEventMessage)
|
||||
return m.id == otherm.id && m.node == otherm.node
|
||||
}
|
||||
|
||||
func (m *networkEventMessage) Message() []byte {
|
||||
return m.msg
|
||||
}
|
||||
|
||||
func (m *networkEventMessage) Finished() {
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) sendNetworkEvent(nid string, event networkEventType, ltime serf.LamportTime) error {
|
||||
nEvent := networkEventData{
|
||||
Event: event,
|
||||
LTime: ltime,
|
||||
NodeName: nDB.config.NodeName,
|
||||
NetworkID: nid,
|
||||
}
|
||||
|
||||
raw, err := encodeMessage(networkEventMsg, &nEvent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nDB.networkBroadcasts.QueueBroadcast(&networkEventMessage{
|
||||
msg: raw,
|
||||
id: nid,
|
||||
node: nDB.config.NodeName,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
type tableEventType uint8
|
||||
|
||||
const (
|
||||
tableEntryCreate tableEventType = 1 + iota
|
||||
tableEntryUpdate
|
||||
tableEntryDelete
|
||||
)
|
||||
|
||||
type tableEventData struct {
|
||||
Event tableEventType
|
||||
LTime serf.LamportTime
|
||||
NetworkID string
|
||||
TableName string
|
||||
NodeName string
|
||||
Value []byte
|
||||
Key string
|
||||
}
|
||||
|
||||
type tableEventMessage struct {
|
||||
id string
|
||||
tname string
|
||||
key string
|
||||
msg []byte
|
||||
node string
|
||||
}
|
||||
|
||||
func (m *tableEventMessage) Invalidates(other memberlist.Broadcast) bool {
|
||||
otherm := other.(*tableEventMessage)
|
||||
return m.id == otherm.id && m.tname == otherm.tname && m.key == otherm.key
|
||||
}
|
||||
|
||||
func (m *tableEventMessage) Message() []byte {
|
||||
return m.msg
|
||||
}
|
||||
|
||||
func (m *tableEventMessage) Finished() {
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) sendTableEvent(event tableEventType, nid string, tname string, key string, entry *entry) error {
|
||||
tEvent := tableEventData{
|
||||
Event: event,
|
||||
LTime: entry.ltime,
|
||||
NodeName: nDB.config.NodeName,
|
||||
NetworkID: nid,
|
||||
TableName: tname,
|
||||
Key: key,
|
||||
Value: entry.value,
|
||||
}
|
||||
|
||||
raw, err := encodeMessage(tableEventMsg, &tEvent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nDB.RLock()
|
||||
broadcastQ := nDB.networks[nDB.config.NodeName][nid].tableBroadcasts
|
||||
nDB.RUnlock()
|
||||
|
||||
broadcastQ.QueueBroadcast(&tableEventMessage{
|
||||
msg: raw,
|
||||
id: nid,
|
||||
tname: tname,
|
||||
key: key,
|
||||
node: nDB.config.NodeName,
|
||||
})
|
||||
return nil
|
||||
}
|
446
vendor/src/github.com/docker/libnetwork/networkdb/cluster.go
vendored
Normal file
446
vendor/src/github.com/docker/libnetwork/networkdb/cluster.go
vendored
Normal file
|
@ -0,0 +1,446 @@
|
|||
package networkdb
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
rnd "math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
const reapInterval = 2 * time.Second
|
||||
|
||||
type logWriter struct{}
|
||||
|
||||
func (l *logWriter) Write(p []byte) (int, error) {
|
||||
str := string(p)
|
||||
|
||||
switch {
|
||||
case strings.Contains(str, "[WARN]"):
|
||||
logrus.Warn(str)
|
||||
case strings.Contains(str, "[DEBUG]"):
|
||||
logrus.Debug(str)
|
||||
case strings.Contains(str, "[INFO]"):
|
||||
logrus.Info(str)
|
||||
case strings.Contains(str, "[ERR]"):
|
||||
logrus.Warn(str)
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) clusterInit() error {
|
||||
config := memberlist.DefaultLANConfig()
|
||||
config.Name = nDB.config.NodeName
|
||||
config.BindAddr = nDB.config.BindAddr
|
||||
|
||||
if nDB.config.BindPort != 0 {
|
||||
config.BindPort = nDB.config.BindPort
|
||||
}
|
||||
|
||||
config.ProtocolVersion = memberlist.ProtocolVersionMax
|
||||
config.Delegate = &delegate{nDB: nDB}
|
||||
config.Events = &eventDelegate{nDB: nDB}
|
||||
config.LogOutput = &logWriter{}
|
||||
|
||||
nDB.networkBroadcasts = &memberlist.TransmitLimitedQueue{
|
||||
NumNodes: func() int {
|
||||
return len(nDB.nodes)
|
||||
},
|
||||
RetransmitMult: config.RetransmitMult,
|
||||
}
|
||||
|
||||
mlist, err := memberlist.Create(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create memberlist: %v", err)
|
||||
}
|
||||
|
||||
nDB.stopCh = make(chan struct{})
|
||||
nDB.memberlist = mlist
|
||||
nDB.mConfig = config
|
||||
|
||||
for _, trigger := range []struct {
|
||||
interval time.Duration
|
||||
fn func()
|
||||
}{
|
||||
{reapInterval, nDB.reapState},
|
||||
{config.GossipInterval, nDB.gossip},
|
||||
{config.PushPullInterval, nDB.bulkSyncTables},
|
||||
} {
|
||||
t := time.NewTicker(trigger.interval)
|
||||
go nDB.triggerFunc(trigger.interval, t.C, nDB.stopCh, trigger.fn)
|
||||
nDB.tickers = append(nDB.tickers, t)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) clusterJoin(members []string) error {
|
||||
mlist := nDB.memberlist
|
||||
|
||||
if _, err := mlist.Join(members); err != nil {
|
||||
return fmt.Errorf("could not join node to memberlist: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) clusterLeave() error {
|
||||
mlist := nDB.memberlist
|
||||
|
||||
if err := mlist.Leave(time.Second); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
close(nDB.stopCh)
|
||||
|
||||
for _, t := range nDB.tickers {
|
||||
t.Stop()
|
||||
}
|
||||
|
||||
return mlist.Shutdown()
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) triggerFunc(stagger time.Duration, C <-chan time.Time, stop <-chan struct{}, f func()) {
|
||||
// Use a random stagger to avoid syncronizing
|
||||
randStagger := time.Duration(uint64(rnd.Int63()) % uint64(stagger))
|
||||
select {
|
||||
case <-time.After(randStagger):
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-C:
|
||||
f()
|
||||
case <-stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) reapState() {
|
||||
nDB.reapNetworks()
|
||||
nDB.reapTableEntries()
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) reapNetworks() {
|
||||
now := time.Now()
|
||||
nDB.Lock()
|
||||
for name, nn := range nDB.networks {
|
||||
for id, n := range nn {
|
||||
if n.leaving && now.Sub(n.leaveTime) > reapInterval {
|
||||
delete(nn, id)
|
||||
nDB.deleteNetworkNode(id, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
nDB.Unlock()
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) reapTableEntries() {
|
||||
var paths []string
|
||||
|
||||
now := time.Now()
|
||||
|
||||
nDB.RLock()
|
||||
nDB.indexes[byTable].Walk(func(path string, v interface{}) bool {
|
||||
entry, ok := v.(*entry)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if !entry.deleting || now.Sub(entry.deleteTime) <= reapInterval {
|
||||
return false
|
||||
}
|
||||
|
||||
paths = append(paths, path)
|
||||
return false
|
||||
})
|
||||
nDB.RUnlock()
|
||||
|
||||
nDB.Lock()
|
||||
for _, path := range paths {
|
||||
params := strings.Split(path[1:], "/")
|
||||
tname := params[0]
|
||||
nid := params[1]
|
||||
key := params[2]
|
||||
|
||||
if _, ok := nDB.indexes[byTable].Delete(fmt.Sprintf("/%s/%s/%s", tname, nid, key)); !ok {
|
||||
logrus.Errorf("Could not delete entry in table %s with network id %s and key %s as it does not exist", tname, nid, key)
|
||||
}
|
||||
|
||||
if _, ok := nDB.indexes[byNetwork].Delete(fmt.Sprintf("/%s/%s/%s", nid, tname, key)); !ok {
|
||||
logrus.Errorf("Could not delete entry in network %s with table name %s and key %s as it does not exist", nid, tname, key)
|
||||
}
|
||||
}
|
||||
nDB.Unlock()
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) gossip() {
|
||||
networkNodes := make(map[string][]string)
|
||||
nDB.RLock()
|
||||
for nid := range nDB.networks[nDB.config.NodeName] {
|
||||
networkNodes[nid] = nDB.networkNodes[nid]
|
||||
|
||||
}
|
||||
nDB.RUnlock()
|
||||
|
||||
for nid, nodes := range networkNodes {
|
||||
mNodes := nDB.mRandomNodes(3, nodes)
|
||||
bytesAvail := udpSendBuf - compoundHeaderOverhead
|
||||
|
||||
nDB.RLock()
|
||||
broadcastQ := nDB.networks[nDB.config.NodeName][nid].tableBroadcasts
|
||||
nDB.RUnlock()
|
||||
|
||||
if broadcastQ == nil {
|
||||
logrus.Errorf("Invalid broadcastQ encountered while gossiping for network %s", nid)
|
||||
continue
|
||||
}
|
||||
|
||||
msgs := broadcastQ.GetBroadcasts(compoundOverhead, bytesAvail)
|
||||
if len(msgs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create a compound message
|
||||
compound := makeCompoundMessage(msgs)
|
||||
|
||||
for _, node := range mNodes {
|
||||
nDB.RLock()
|
||||
mnode := nDB.nodes[node]
|
||||
nDB.RUnlock()
|
||||
|
||||
if mnode == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Send the compound message
|
||||
if err := nDB.memberlist.SendToUDP(mnode, compound.Bytes()); err != nil {
|
||||
logrus.Errorf("Failed to send gossip to %s: %s", mnode.Addr, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type bulkSyncMessage struct {
|
||||
LTime serf.LamportTime
|
||||
Unsolicited bool
|
||||
NodeName string
|
||||
Networks []string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) bulkSyncTables() {
|
||||
var networks []string
|
||||
nDB.RLock()
|
||||
for nid := range nDB.networks[nDB.config.NodeName] {
|
||||
networks = append(networks, nid)
|
||||
}
|
||||
nDB.RUnlock()
|
||||
|
||||
for {
|
||||
if len(networks) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
nid := networks[0]
|
||||
networks = networks[1:]
|
||||
|
||||
completed, err := nDB.bulkSync(nid, false)
|
||||
if err != nil {
|
||||
logrus.Errorf("periodic bulk sync failure for network %s: %v", nid, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove all the networks for which we have
|
||||
// successfully completed bulk sync in this iteration.
|
||||
updatedNetworks := make([]string, 0, len(networks))
|
||||
for _, nid := range networks {
|
||||
for _, completedNid := range completed {
|
||||
if nid == completedNid {
|
||||
continue
|
||||
}
|
||||
|
||||
updatedNetworks = append(updatedNetworks, nid)
|
||||
}
|
||||
}
|
||||
|
||||
networks = updatedNetworks
|
||||
}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) bulkSync(nid string, all bool) ([]string, error) {
|
||||
nDB.RLock()
|
||||
nodes := nDB.networkNodes[nid]
|
||||
nDB.RUnlock()
|
||||
|
||||
if !all {
|
||||
// If not all, then just pick one.
|
||||
nodes = nDB.mRandomNodes(1, nodes)
|
||||
}
|
||||
|
||||
logrus.Debugf("%s: Initiating bulk sync with nodes %v", nDB.config.NodeName, nodes)
|
||||
var err error
|
||||
var networks []string
|
||||
for _, node := range nodes {
|
||||
if node == nDB.config.NodeName {
|
||||
continue
|
||||
}
|
||||
|
||||
networks = nDB.findCommonNetworks(node)
|
||||
err = nDB.bulkSyncNode(networks, node, true)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("bulk sync failed on node %s: %v", node, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return networks, nil
|
||||
}
|
||||
|
||||
// Bulk sync all the table entries belonging to a set of networks to a
|
||||
// single peer node. It can be unsolicited or can be in response to an
|
||||
// unsolicited bulk sync
|
||||
func (nDB *NetworkDB) bulkSyncNode(networks []string, node string, unsolicited bool) error {
|
||||
var msgs [][]byte
|
||||
|
||||
logrus.Debugf("%s: Initiating bulk sync for networks %v with node %s", nDB.config.NodeName, networks, node)
|
||||
|
||||
nDB.RLock()
|
||||
mnode := nDB.nodes[node]
|
||||
if mnode == nil {
|
||||
nDB.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, nid := range networks {
|
||||
nDB.indexes[byNetwork].WalkPrefix(fmt.Sprintf("/%s", nid), func(path string, v interface{}) bool {
|
||||
entry, ok := v.(*entry)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
params := strings.Split(path[1:], "/")
|
||||
tEvent := tableEventData{
|
||||
Event: tableEntryCreate,
|
||||
LTime: entry.ltime,
|
||||
NodeName: entry.node,
|
||||
NetworkID: nid,
|
||||
TableName: params[1],
|
||||
Key: params[2],
|
||||
Value: entry.value,
|
||||
}
|
||||
|
||||
msg, err := encodeMessage(tableEventMsg, &tEvent)
|
||||
if err != nil {
|
||||
logrus.Errorf("Encode failure during bulk sync: %#v", tEvent)
|
||||
return false
|
||||
}
|
||||
|
||||
msgs = append(msgs, msg)
|
||||
return false
|
||||
})
|
||||
}
|
||||
nDB.RUnlock()
|
||||
|
||||
// Create a compound message
|
||||
compound := makeCompoundMessage(msgs)
|
||||
|
||||
bsm := bulkSyncMessage{
|
||||
LTime: nDB.tableClock.Time(),
|
||||
Unsolicited: unsolicited,
|
||||
NodeName: nDB.config.NodeName,
|
||||
Networks: networks,
|
||||
Payload: compound.Bytes(),
|
||||
}
|
||||
|
||||
buf, err := encodeMessage(bulkSyncMsg, &bsm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encode bulk sync message: %v", err)
|
||||
}
|
||||
|
||||
nDB.Lock()
|
||||
ch := make(chan struct{})
|
||||
nDB.bulkSyncAckTbl[node] = ch
|
||||
nDB.Unlock()
|
||||
|
||||
err = nDB.memberlist.SendToTCP(mnode, buf)
|
||||
if err != nil {
|
||||
nDB.Lock()
|
||||
delete(nDB.bulkSyncAckTbl, node)
|
||||
nDB.Unlock()
|
||||
|
||||
return fmt.Errorf("failed to send a TCP message during bulk sync: %v", err)
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
select {
|
||||
case <-time.After(30 * time.Second):
|
||||
logrus.Errorf("Bulk sync to node %s timed out", node)
|
||||
case <-ch:
|
||||
nDB.Lock()
|
||||
delete(nDB.bulkSyncAckTbl, node)
|
||||
nDB.Unlock()
|
||||
|
||||
logrus.Debugf("%s: Bulk sync to node %s took %s", nDB.config.NodeName, node, time.Now().Sub(startTime))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns a random offset between 0 and n
|
||||
func randomOffset(n int) int {
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
val, err := rand.Int(rand.Reader, big.NewInt(int64(n)))
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to get a random offset: %v", err)
|
||||
return 0
|
||||
}
|
||||
|
||||
return int(val.Int64())
|
||||
}
|
||||
|
||||
// mRandomNodes is used to select up to m random nodes. It is possible
|
||||
// that less than m nodes are returned.
|
||||
func (nDB *NetworkDB) mRandomNodes(m int, nodes []string) []string {
|
||||
n := len(nodes)
|
||||
mNodes := make([]string, 0, m)
|
||||
OUTER:
|
||||
// Probe up to 3*n times, with large n this is not necessary
|
||||
// since k << n, but with small n we want search to be
|
||||
// exhaustive
|
||||
for i := 0; i < 3*n && len(mNodes) < m; i++ {
|
||||
// Get random node
|
||||
idx := randomOffset(n)
|
||||
node := nodes[idx]
|
||||
|
||||
if node == nDB.config.NodeName {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we have this node already
|
||||
for j := 0; j < len(mNodes); j++ {
|
||||
if node == mNodes[j] {
|
||||
continue OUTER
|
||||
}
|
||||
}
|
||||
|
||||
// Append the node
|
||||
mNodes = append(mNodes, node)
|
||||
}
|
||||
|
||||
return mNodes
|
||||
}
|
315
vendor/src/github.com/docker/libnetwork/networkdb/delegate.go
vendored
Normal file
315
vendor/src/github.com/docker/libnetwork/networkdb/delegate.go
vendored
Normal file
|
@ -0,0 +1,315 @@
|
|||
package networkdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
type networkData struct {
|
||||
LTime serf.LamportTime
|
||||
ID string
|
||||
NodeName string
|
||||
Leaving bool
|
||||
}
|
||||
|
||||
type networkPushPull struct {
|
||||
LTime serf.LamportTime
|
||||
Networks []networkData
|
||||
}
|
||||
|
||||
type delegate struct {
|
||||
nDB *NetworkDB
|
||||
}
|
||||
|
||||
func (d *delegate) NodeMeta(limit int) []byte {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleNetworkEvent(nEvent *networkEventData) bool {
|
||||
// Update our local clock if the received messages has newer
|
||||
// time.
|
||||
nDB.networkClock.Witness(nEvent.LTime)
|
||||
|
||||
nDB.Lock()
|
||||
defer nDB.Unlock()
|
||||
|
||||
nodeNetworks, ok := nDB.networks[nEvent.NodeName]
|
||||
if !ok {
|
||||
// We haven't heard about this node at all. Ignore the leave
|
||||
if nEvent.Event == networkLeave {
|
||||
return false
|
||||
}
|
||||
|
||||
nodeNetworks = make(map[string]*network)
|
||||
nDB.networks[nEvent.NodeName] = nodeNetworks
|
||||
}
|
||||
|
||||
if n, ok := nodeNetworks[nEvent.NetworkID]; ok {
|
||||
// We have the latest state. Ignore the event
|
||||
// since it is stale.
|
||||
if n.ltime >= nEvent.LTime {
|
||||
return false
|
||||
}
|
||||
|
||||
n.ltime = nEvent.LTime
|
||||
n.leaving = nEvent.Event == networkLeave
|
||||
if n.leaving {
|
||||
n.leaveTime = time.Now()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if nEvent.Event == networkLeave {
|
||||
return false
|
||||
}
|
||||
|
||||
// This remote network join is being seen the first time.
|
||||
nodeNetworks[nEvent.NetworkID] = &network{
|
||||
id: nEvent.NetworkID,
|
||||
ltime: nEvent.LTime,
|
||||
}
|
||||
|
||||
nDB.networkNodes[nEvent.NetworkID] = append(nDB.networkNodes[nEvent.NetworkID], nEvent.NodeName)
|
||||
return true
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleTableEvent(tEvent *tableEventData) bool {
|
||||
// Update our local clock if the received messages has newer
|
||||
// time.
|
||||
nDB.tableClock.Witness(tEvent.LTime)
|
||||
|
||||
if entry, err := nDB.getEntry(tEvent.TableName, tEvent.NetworkID, tEvent.Key); err == nil {
|
||||
// We have the latest state. Ignore the event
|
||||
// since it is stale.
|
||||
if entry.ltime >= tEvent.LTime {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
entry := &entry{
|
||||
ltime: tEvent.LTime,
|
||||
node: tEvent.NodeName,
|
||||
value: tEvent.Value,
|
||||
deleting: tEvent.Event == tableEntryDelete,
|
||||
}
|
||||
|
||||
if entry.deleting {
|
||||
entry.deleteTime = time.Now()
|
||||
}
|
||||
|
||||
nDB.Lock()
|
||||
nDB.indexes[byTable].Insert(fmt.Sprintf("/%s/%s/%s", tEvent.TableName, tEvent.NetworkID, tEvent.Key), entry)
|
||||
nDB.indexes[byNetwork].Insert(fmt.Sprintf("/%s/%s/%s", tEvent.NetworkID, tEvent.TableName, tEvent.Key), entry)
|
||||
nDB.Unlock()
|
||||
|
||||
var op opType
|
||||
switch tEvent.Event {
|
||||
case tableEntryCreate:
|
||||
op = opCreate
|
||||
case tableEntryUpdate:
|
||||
op = opUpdate
|
||||
case tableEntryDelete:
|
||||
op = opDelete
|
||||
}
|
||||
|
||||
nDB.broadcaster.Write(makeEvent(op, tEvent.TableName, tEvent.NetworkID, tEvent.Key, tEvent.Value))
|
||||
return true
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleCompound(buf []byte) {
|
||||
// Decode the parts
|
||||
trunc, parts, err := decodeCompoundMessage(buf[1:])
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to decode compound request: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Log any truncation
|
||||
if trunc > 0 {
|
||||
logrus.Warnf("Compound request had %d truncated messages", trunc)
|
||||
}
|
||||
|
||||
// Handle each message
|
||||
for _, part := range parts {
|
||||
nDB.handleMessage(part)
|
||||
}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleTableMessage(buf []byte) {
|
||||
var tEvent tableEventData
|
||||
if err := decodeMessage(buf[1:], &tEvent); err != nil {
|
||||
logrus.Errorf("Error decoding table event message: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if rebroadcast := nDB.handleTableEvent(&tEvent); rebroadcast {
|
||||
// Copy the buffer since we cannot rely on the slice not changing
|
||||
newBuf := make([]byte, len(buf))
|
||||
copy(newBuf, buf)
|
||||
|
||||
nDB.RLock()
|
||||
n, ok := nDB.networks[nDB.config.NodeName][tEvent.NetworkID]
|
||||
nDB.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
broadcastQ := n.tableBroadcasts
|
||||
broadcastQ.QueueBroadcast(&tableEventMessage{
|
||||
msg: newBuf,
|
||||
id: tEvent.NetworkID,
|
||||
tname: tEvent.TableName,
|
||||
key: tEvent.Key,
|
||||
node: nDB.config.NodeName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleNetworkMessage(buf []byte) {
|
||||
var nEvent networkEventData
|
||||
if err := decodeMessage(buf[1:], &nEvent); err != nil {
|
||||
logrus.Errorf("Error decoding network event message: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if rebroadcast := nDB.handleNetworkEvent(&nEvent); rebroadcast {
|
||||
// Copy the buffer since it we cannot rely on the slice not changing
|
||||
newBuf := make([]byte, len(buf))
|
||||
copy(newBuf, buf)
|
||||
|
||||
nDB.networkBroadcasts.QueueBroadcast(&networkEventMessage{
|
||||
msg: newBuf,
|
||||
id: nEvent.NetworkID,
|
||||
node: nEvent.NodeName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleBulkSync(buf []byte) {
|
||||
var bsm bulkSyncMessage
|
||||
if err := decodeMessage(buf[1:], &bsm); err != nil {
|
||||
logrus.Errorf("Error decoding bulk sync message: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if bsm.LTime > 0 {
|
||||
nDB.tableClock.Witness(bsm.LTime)
|
||||
}
|
||||
|
||||
nDB.handleMessage(bsm.Payload)
|
||||
|
||||
// Don't respond to a bulk sync which was not unsolicited
|
||||
if !bsm.Unsolicited {
|
||||
nDB.RLock()
|
||||
ch, ok := nDB.bulkSyncAckTbl[bsm.NodeName]
|
||||
nDB.RUnlock()
|
||||
if ok {
|
||||
close(ch)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if err := nDB.bulkSyncNode(bsm.Networks, bsm.NodeName, false); err != nil {
|
||||
logrus.Errorf("Error in responding to bulk sync from node %s: %v", nDB.nodes[bsm.NodeName].Addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) handleMessage(buf []byte) {
|
||||
msgType := messageType(buf[0])
|
||||
|
||||
switch msgType {
|
||||
case networkEventMsg:
|
||||
nDB.handleNetworkMessage(buf)
|
||||
case tableEventMsg:
|
||||
nDB.handleTableMessage(buf)
|
||||
case compoundMsg:
|
||||
nDB.handleCompound(buf)
|
||||
case bulkSyncMsg:
|
||||
nDB.handleBulkSync(buf)
|
||||
default:
|
||||
logrus.Errorf("%s: unknown message type %d payload = %v", nDB.config.NodeName, msgType, buf[:8])
|
||||
}
|
||||
}
|
||||
|
||||
func (d *delegate) NotifyMsg(buf []byte) {
|
||||
if len(buf) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
d.nDB.handleMessage(buf)
|
||||
}
|
||||
|
||||
func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte {
|
||||
return d.nDB.networkBroadcasts.GetBroadcasts(overhead, limit)
|
||||
}
|
||||
|
||||
func (d *delegate) LocalState(join bool) []byte {
|
||||
d.nDB.RLock()
|
||||
defer d.nDB.RUnlock()
|
||||
|
||||
pp := networkPushPull{
|
||||
LTime: d.nDB.networkClock.Time(),
|
||||
}
|
||||
|
||||
for name, nn := range d.nDB.networks {
|
||||
for _, n := range nn {
|
||||
pp.Networks = append(pp.Networks, networkData{
|
||||
LTime: n.ltime,
|
||||
ID: n.id,
|
||||
NodeName: name,
|
||||
Leaving: n.leaving,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := encodeMessage(networkPushPullMsg, &pp)
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to encode local network state: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
func (d *delegate) MergeRemoteState(buf []byte, isJoin bool) {
|
||||
if len(buf) == 0 {
|
||||
logrus.Error("zero byte remote network state received")
|
||||
return
|
||||
}
|
||||
|
||||
if messageType(buf[0]) != networkPushPullMsg {
|
||||
logrus.Errorf("Invalid message type %v received from remote", buf[0])
|
||||
}
|
||||
|
||||
pp := networkPushPull{}
|
||||
if err := decodeMessage(buf[1:], &pp); err != nil {
|
||||
logrus.Errorf("Failed to decode remote network state: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if pp.LTime > 0 {
|
||||
d.nDB.networkClock.Witness(pp.LTime)
|
||||
}
|
||||
|
||||
for _, n := range pp.Networks {
|
||||
nEvent := &networkEventData{
|
||||
LTime: n.LTime,
|
||||
NodeName: n.NodeName,
|
||||
NetworkID: n.ID,
|
||||
Event: networkJoin,
|
||||
}
|
||||
|
||||
if n.Leaving {
|
||||
nEvent.Event = networkLeave
|
||||
}
|
||||
|
||||
d.nDB.handleNetworkEvent(nEvent)
|
||||
}
|
||||
|
||||
}
|
23
vendor/src/github.com/docker/libnetwork/networkdb/event_delegate.go
vendored
Normal file
23
vendor/src/github.com/docker/libnetwork/networkdb/event_delegate.go
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package networkdb
|
||||
|
||||
import "github.com/hashicorp/memberlist"
|
||||
|
||||
type eventDelegate struct {
|
||||
nDB *NetworkDB
|
||||
}
|
||||
|
||||
func (e *eventDelegate) NotifyJoin(n *memberlist.Node) {
|
||||
e.nDB.Lock()
|
||||
e.nDB.nodes[n.Name] = n
|
||||
e.nDB.Unlock()
|
||||
}
|
||||
|
||||
func (e *eventDelegate) NotifyLeave(n *memberlist.Node) {
|
||||
e.nDB.deleteNodeTableEntries(n.Name)
|
||||
e.nDB.Lock()
|
||||
delete(e.nDB.nodes, n.Name)
|
||||
e.nDB.Unlock()
|
||||
}
|
||||
|
||||
func (e *eventDelegate) NotifyUpdate(n *memberlist.Node) {
|
||||
}
|
122
vendor/src/github.com/docker/libnetwork/networkdb/message.go
vendored
Normal file
122
vendor/src/github.com/docker/libnetwork/networkdb/message.go
vendored
Normal file
|
@ -0,0 +1,122 @@
|
|||
package networkdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/go-msgpack/codec"
|
||||
)
|
||||
|
||||
type messageType uint8
|
||||
|
||||
const (
|
||||
// For network join/leave event message
|
||||
networkEventMsg messageType = 1 + iota
|
||||
|
||||
// For pushing/pulling network/node association state
|
||||
networkPushPullMsg
|
||||
|
||||
// For table entry CRUD event message
|
||||
tableEventMsg
|
||||
|
||||
// For building a compound message which packs many different
|
||||
// message types together
|
||||
compoundMsg
|
||||
|
||||
// For syncing table entries in bulk b/w nodes.
|
||||
bulkSyncMsg
|
||||
)
|
||||
|
||||
const (
|
||||
// Max udp message size chosen to avoid network packet
|
||||
// fragmentation.
|
||||
udpSendBuf = 1400
|
||||
|
||||
// Compound message header overhead 1 byte(message type) + 4
|
||||
// bytes (num messages)
|
||||
compoundHeaderOverhead = 5
|
||||
|
||||
// Overhead for each embedded message in a compound message 2
|
||||
// bytes (len of embedded message)
|
||||
compoundOverhead = 2
|
||||
)
|
||||
|
||||
func decodeMessage(buf []byte, out interface{}) error {
|
||||
var handle codec.MsgpackHandle
|
||||
return codec.NewDecoder(bytes.NewReader(buf), &handle).Decode(out)
|
||||
}
|
||||
|
||||
func encodeMessage(t messageType, msg interface{}) ([]byte, error) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.WriteByte(uint8(t))
|
||||
|
||||
handle := codec.MsgpackHandle{}
|
||||
encoder := codec.NewEncoder(buf, &handle)
|
||||
err := encoder.Encode(msg)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
// makeCompoundMessage takes a list of messages and generates
|
||||
// a single compound message containing all of them
|
||||
func makeCompoundMessage(msgs [][]byte) *bytes.Buffer {
|
||||
// Create a local buffer
|
||||
buf := bytes.NewBuffer(nil)
|
||||
|
||||
// Write out the type
|
||||
buf.WriteByte(uint8(compoundMsg))
|
||||
|
||||
// Write out the number of message
|
||||
binary.Write(buf, binary.BigEndian, uint32(len(msgs)))
|
||||
|
||||
// Add the message lengths
|
||||
for _, m := range msgs {
|
||||
binary.Write(buf, binary.BigEndian, uint16(len(m)))
|
||||
}
|
||||
|
||||
// Append the messages
|
||||
for _, m := range msgs {
|
||||
buf.Write(m)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
// decodeCompoundMessage splits a compound message and returns
|
||||
// the slices of individual messages. Also returns the number
|
||||
// of truncated messages and any potential error
|
||||
func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) {
|
||||
if len(buf) < 1 {
|
||||
err = fmt.Errorf("missing compound length byte")
|
||||
return
|
||||
}
|
||||
numParts := binary.BigEndian.Uint32(buf[0:4])
|
||||
buf = buf[4:]
|
||||
|
||||
// Check we have enough bytes
|
||||
if len(buf) < int(numParts*2) {
|
||||
err = fmt.Errorf("truncated len slice")
|
||||
return
|
||||
}
|
||||
|
||||
// Decode the lengths
|
||||
lengths := make([]uint16, numParts)
|
||||
for i := 0; i < int(numParts); i++ {
|
||||
lengths[i] = binary.BigEndian.Uint16(buf[i*2 : i*2+2])
|
||||
}
|
||||
buf = buf[numParts*2:]
|
||||
|
||||
// Split each message
|
||||
for idx, msgLen := range lengths {
|
||||
if len(buf) < int(msgLen) {
|
||||
trunc = int(numParts) - idx
|
||||
return
|
||||
}
|
||||
|
||||
// Extract the slice, seek past on the buffer
|
||||
slice := buf[:msgLen]
|
||||
buf = buf[msgLen:]
|
||||
parts = append(parts, slice)
|
||||
}
|
||||
return
|
||||
}
|
424
vendor/src/github.com/docker/libnetwork/networkdb/networkdb.go
vendored
Normal file
424
vendor/src/github.com/docker/libnetwork/networkdb/networkdb.go
vendored
Normal file
|
@ -0,0 +1,424 @@
|
|||
package networkdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/armon/go-radix"
|
||||
"github.com/docker/go-events"
|
||||
"github.com/hashicorp/memberlist"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
const (
|
||||
byTable int = 1 + iota
|
||||
byNetwork
|
||||
)
|
||||
|
||||
// NetworkDB instance drives the networkdb cluster and acts the broker
|
||||
// for cluster-scoped and network-scoped gossip and watches.
|
||||
type NetworkDB struct {
|
||||
sync.RWMutex
|
||||
|
||||
// NetworkDB configuration.
|
||||
config *Config
|
||||
|
||||
// local copy of memberlist config that we use to driver
|
||||
// network scoped gossip and bulk sync.
|
||||
mConfig *memberlist.Config
|
||||
|
||||
// All the tree index (byTable, byNetwork) that we maintain
|
||||
// the db.
|
||||
indexes map[int]*radix.Tree
|
||||
|
||||
// Memberlist we use to drive the cluster.
|
||||
memberlist *memberlist.Memberlist
|
||||
|
||||
// List of all peer nodes in the cluster not-limited to any
|
||||
// network.
|
||||
nodes map[string]*memberlist.Node
|
||||
|
||||
// A multi-dimensional map of network/node attachmemts. The
|
||||
// first key is a node name and the second key is a network ID
|
||||
// for the network that node is participating in.
|
||||
networks map[string]map[string]*network
|
||||
|
||||
// A map of nodes which are participating in a given
|
||||
// network. The key is a network ID.
|
||||
|
||||
networkNodes map[string][]string
|
||||
|
||||
// A table of ack channels for every node from which we are
|
||||
// waiting for an ack.
|
||||
bulkSyncAckTbl map[string]chan struct{}
|
||||
|
||||
// Global lamport clock for node network attach events.
|
||||
networkClock serf.LamportClock
|
||||
|
||||
// Global lamport clock for table events.
|
||||
tableClock serf.LamportClock
|
||||
|
||||
// Broadcast queue for network event gossip.
|
||||
networkBroadcasts *memberlist.TransmitLimitedQueue
|
||||
|
||||
// A central stop channel to stop all go routines running on
|
||||
// behalf of the NetworkDB instance.
|
||||
stopCh chan struct{}
|
||||
|
||||
// A central broadcaster for all local watchers watching table
|
||||
// events.
|
||||
broadcaster *events.Broadcaster
|
||||
|
||||
// List of all tickers which needed to be stopped when
|
||||
// cleaning up.
|
||||
tickers []*time.Ticker
|
||||
}
|
||||
|
||||
// network describes the node/network attachment.
|
||||
type network struct {
|
||||
// Network ID
|
||||
id string
|
||||
|
||||
// Lamport time for the latest state of the entry.
|
||||
ltime serf.LamportTime
|
||||
|
||||
// Node leave is in progress.
|
||||
leaving bool
|
||||
|
||||
// The time this node knew about the node's network leave.
|
||||
leaveTime time.Time
|
||||
|
||||
// The broadcast queue for table event gossip. This is only
|
||||
// initialized for this node's network attachment entries.
|
||||
tableBroadcasts *memberlist.TransmitLimitedQueue
|
||||
}
|
||||
|
||||
// Config represents the configuration of the networdb instance and
|
||||
// can be passed by the caller.
|
||||
type Config struct {
|
||||
// NodeName is the cluster wide unique name for this node.
|
||||
NodeName string
|
||||
|
||||
// BindAddr is the local node's IP address that we bind to for
|
||||
// cluster communication.
|
||||
BindAddr string
|
||||
|
||||
// BindPort is the local node's port to which we bind to for
|
||||
// cluster communication.
|
||||
BindPort int
|
||||
}
|
||||
|
||||
// entry defines a table entry
|
||||
type entry struct {
|
||||
// node from which this entry was learned.
|
||||
node string
|
||||
|
||||
// Lamport time for the most recent update to the entry
|
||||
ltime serf.LamportTime
|
||||
|
||||
// Opaque value store in the entry
|
||||
value []byte
|
||||
|
||||
// Deleting the entry is in progress. All entries linger in
|
||||
// the cluster for certain amount of time after deletion.
|
||||
deleting bool
|
||||
|
||||
// The wall clock time when this node learned about this deletion.
|
||||
deleteTime time.Time
|
||||
}
|
||||
|
||||
// New creates a new instance of NetworkDB using the Config passed by
|
||||
// the caller.
|
||||
func New(c *Config) (*NetworkDB, error) {
|
||||
nDB := &NetworkDB{
|
||||
config: c,
|
||||
indexes: make(map[int]*radix.Tree),
|
||||
networks: make(map[string]map[string]*network),
|
||||
nodes: make(map[string]*memberlist.Node),
|
||||
networkNodes: make(map[string][]string),
|
||||
bulkSyncAckTbl: make(map[string]chan struct{}),
|
||||
broadcaster: events.NewBroadcaster(),
|
||||
}
|
||||
|
||||
nDB.indexes[byTable] = radix.New()
|
||||
nDB.indexes[byNetwork] = radix.New()
|
||||
|
||||
if err := nDB.clusterInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nDB, nil
|
||||
}
|
||||
|
||||
// Join joins this NetworkDB instance with a list of peer NetworkDB
|
||||
// instances passed by the caller in the form of addr:port
|
||||
func (nDB *NetworkDB) Join(members []string) error {
|
||||
return nDB.clusterJoin(members)
|
||||
}
|
||||
|
||||
// Close destroys this NetworkDB instance by leave the cluster,
|
||||
// stopping timers, canceling goroutines etc.
|
||||
func (nDB *NetworkDB) Close() {
|
||||
if err := nDB.clusterLeave(); err != nil {
|
||||
logrus.Errorf("Could not close DB %s: %v", nDB.config.NodeName, err)
|
||||
}
|
||||
}
|
||||
|
||||
// GetEntry retrieves the value of a table entry in a given (network,
|
||||
// table, key) tuple
|
||||
func (nDB *NetworkDB) GetEntry(tname, nid, key string) ([]byte, error) {
|
||||
entry, err := nDB.getEntry(tname, nid, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return entry.value, nil
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) getEntry(tname, nid, key string) (*entry, error) {
|
||||
nDB.RLock()
|
||||
defer nDB.RUnlock()
|
||||
|
||||
e, ok := nDB.indexes[byTable].Get(fmt.Sprintf("/%s/%s/%s", tname, nid, key))
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not get entry in table %s with network id %s and key %s", tname, nid, key)
|
||||
}
|
||||
|
||||
return e.(*entry), nil
|
||||
}
|
||||
|
||||
// CreateEntry creates a table entry in NetworkDB for given (network,
|
||||
// table, key) tuple and if the NetworkDB is part of the cluster
|
||||
// propogates this event to the cluster. It is an error to create an
|
||||
// entry for the same tuple for which there is already an existing
|
||||
// entry.
|
||||
func (nDB *NetworkDB) CreateEntry(tname, nid, key string, value []byte) error {
|
||||
if _, err := nDB.GetEntry(tname, nid, key); err == nil {
|
||||
return fmt.Errorf("cannot create entry as the entry in table %s with network id %s and key %s already exists", tname, nid, key)
|
||||
}
|
||||
|
||||
entry := &entry{
|
||||
ltime: nDB.tableClock.Increment(),
|
||||
node: nDB.config.NodeName,
|
||||
value: value,
|
||||
}
|
||||
|
||||
if err := nDB.sendTableEvent(tableEntryCreate, nid, tname, key, entry); err != nil {
|
||||
return fmt.Errorf("cannot send table create event: %v", err)
|
||||
}
|
||||
|
||||
nDB.Lock()
|
||||
nDB.indexes[byTable].Insert(fmt.Sprintf("/%s/%s/%s", tname, nid, key), entry)
|
||||
nDB.indexes[byNetwork].Insert(fmt.Sprintf("/%s/%s/%s", nid, tname, key), entry)
|
||||
nDB.Unlock()
|
||||
|
||||
nDB.broadcaster.Write(makeEvent(opCreate, tname, nid, key, value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEntry updates a table entry in NetworkDB for given (network,
|
||||
// table, key) tuple and if the NetworkDB is part of the cluster
|
||||
// propogates this event to the cluster. It is an error to update a
|
||||
// non-existent entry.
|
||||
func (nDB *NetworkDB) UpdateEntry(tname, nid, key string, value []byte) error {
|
||||
if _, err := nDB.GetEntry(tname, nid, key); err != nil {
|
||||
return fmt.Errorf("cannot update entry as the entry in table %s with network id %s and key %s does not exist", tname, nid, key)
|
||||
}
|
||||
|
||||
entry := &entry{
|
||||
ltime: nDB.tableClock.Increment(),
|
||||
node: nDB.config.NodeName,
|
||||
value: value,
|
||||
}
|
||||
|
||||
if err := nDB.sendTableEvent(tableEntryUpdate, nid, tname, key, entry); err != nil {
|
||||
return fmt.Errorf("cannot send table update event: %v", err)
|
||||
}
|
||||
|
||||
nDB.Lock()
|
||||
nDB.indexes[byTable].Insert(fmt.Sprintf("/%s/%s/%s", tname, nid, key), entry)
|
||||
nDB.indexes[byNetwork].Insert(fmt.Sprintf("/%s/%s/%s", nid, tname, key), entry)
|
||||
nDB.Unlock()
|
||||
|
||||
nDB.broadcaster.Write(makeEvent(opUpdate, tname, nid, key, value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEntry deletes a table entry in NetworkDB for given (network,
|
||||
// table, key) tuple and if the NetworkDB is part of the cluster
|
||||
// propogates this event to the cluster.
|
||||
func (nDB *NetworkDB) DeleteEntry(tname, nid, key string) error {
|
||||
value, err := nDB.GetEntry(tname, nid, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete entry as the entry in table %s with network id %s and key %s does not exist", tname, nid, key)
|
||||
}
|
||||
|
||||
entry := &entry{
|
||||
ltime: nDB.tableClock.Increment(),
|
||||
node: nDB.config.NodeName,
|
||||
value: value,
|
||||
deleting: true,
|
||||
deleteTime: time.Now(),
|
||||
}
|
||||
|
||||
if err := nDB.sendTableEvent(tableEntryDelete, nid, tname, key, entry); err != nil {
|
||||
return fmt.Errorf("cannot send table delete event: %v", err)
|
||||
}
|
||||
|
||||
nDB.Lock()
|
||||
nDB.indexes[byTable].Insert(fmt.Sprintf("/%s/%s/%s", tname, nid, key), entry)
|
||||
nDB.indexes[byNetwork].Insert(fmt.Sprintf("/%s/%s/%s", nid, tname, key), entry)
|
||||
nDB.Unlock()
|
||||
|
||||
nDB.broadcaster.Write(makeEvent(opDelete, tname, nid, key, value))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nDB *NetworkDB) deleteNodeTableEntries(node string) {
|
||||
nDB.Lock()
|
||||
nDB.indexes[byTable].Walk(func(path string, v interface{}) bool {
|
||||
oldEntry := v.(*entry)
|
||||
if oldEntry.node != node {
|
||||
return false
|
||||
}
|
||||
|
||||
params := strings.Split(path[1:], "/")
|
||||
tname := params[0]
|
||||
nid := params[1]
|
||||
key := params[2]
|
||||
|
||||
entry := &entry{
|
||||
ltime: oldEntry.ltime,
|
||||
node: node,
|
||||
value: oldEntry.value,
|
||||
deleting: true,
|
||||
deleteTime: time.Now(),
|
||||
}
|
||||
|
||||
nDB.indexes[byTable].Insert(fmt.Sprintf("/%s/%s/%s", tname, nid, key), entry)
|
||||
nDB.indexes[byNetwork].Insert(fmt.Sprintf("/%s/%s/%s", nid, tname, key), entry)
|
||||
return false
|
||||
})
|
||||
nDB.Unlock()
|
||||
}
|
||||
|
||||
// WalkTable walks a single table in NetworkDB and invokes the passed
|
||||
// function for each entry in the table passing the network, key,
|
||||
// value. The walk stops if the passed function returns a true.
|
||||
func (nDB *NetworkDB) WalkTable(tname string, fn func(string, string, []byte) bool) error {
|
||||
nDB.RLock()
|
||||
values := make(map[string]interface{})
|
||||
nDB.indexes[byTable].WalkPrefix(fmt.Sprintf("/%s", tname), func(path string, v interface{}) bool {
|
||||
values[path] = v
|
||||
return false
|
||||
})
|
||||
nDB.RUnlock()
|
||||
|
||||
for k, v := range values {
|
||||
params := strings.Split(k[1:], "/")
|
||||
nid := params[1]
|
||||
key := params[2]
|
||||
if fn(nid, key, v.(*entry).value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// JoinNetwork joins this node to a given network and propogates this
|
||||
// event across the cluster. This triggers this node joining the
|
||||
// sub-cluster of this network and participates in the network-scoped
|
||||
// gossip and bulk sync for this network.
|
||||
func (nDB *NetworkDB) JoinNetwork(nid string) error {
|
||||
ltime := nDB.networkClock.Increment()
|
||||
|
||||
nDB.Lock()
|
||||
nodeNetworks, ok := nDB.networks[nDB.config.NodeName]
|
||||
if !ok {
|
||||
nodeNetworks = make(map[string]*network)
|
||||
nDB.networks[nDB.config.NodeName] = nodeNetworks
|
||||
}
|
||||
nodeNetworks[nid] = &network{id: nid, ltime: ltime}
|
||||
nodeNetworks[nid].tableBroadcasts = &memberlist.TransmitLimitedQueue{
|
||||
NumNodes: func() int {
|
||||
return len(nDB.networkNodes[nid])
|
||||
},
|
||||
RetransmitMult: 4,
|
||||
}
|
||||
nDB.networkNodes[nid] = append(nDB.networkNodes[nid], nDB.config.NodeName)
|
||||
nDB.Unlock()
|
||||
|
||||
if err := nDB.sendNetworkEvent(nid, networkJoin, ltime); err != nil {
|
||||
return fmt.Errorf("failed to send leave network event for %s: %v", nid, err)
|
||||
}
|
||||
|
||||
logrus.Debugf("%s: joined network %s", nDB.config.NodeName, nid)
|
||||
if _, err := nDB.bulkSync(nid, true); err != nil {
|
||||
logrus.Errorf("Error bulk syncing while joining network %s: %v", nid, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LeaveNetwork leaves this node from a given network and propogates
|
||||
// this event across the cluster. This triggers this node leaving the
|
||||
// sub-cluster of this network and as a result will no longer
|
||||
// participate in the network-scoped gossip and bulk sync for this
|
||||
// network.
|
||||
func (nDB *NetworkDB) LeaveNetwork(nid string) error {
|
||||
ltime := nDB.networkClock.Increment()
|
||||
if err := nDB.sendNetworkEvent(nid, networkLeave, ltime); err != nil {
|
||||
return fmt.Errorf("failed to send leave network event for %s: %v", nid, err)
|
||||
}
|
||||
|
||||
nDB.Lock()
|
||||
defer nDB.Unlock()
|
||||
nodeNetworks, ok := nDB.networks[nDB.config.NodeName]
|
||||
if !ok {
|
||||
return fmt.Errorf("could not find self node for network %s while trying to leave", nid)
|
||||
}
|
||||
|
||||
n, ok := nodeNetworks[nid]
|
||||
if !ok {
|
||||
return fmt.Errorf("could not find network %s while trying to leave", nid)
|
||||
}
|
||||
|
||||
n.ltime = ltime
|
||||
n.leaving = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes the node from the list of nodes which participate in the
|
||||
// passed network. Caller should hold the NetworkDB lock while calling
|
||||
// this
|
||||
func (nDB *NetworkDB) deleteNetworkNode(nid string, nodeName string) {
|
||||
nodes := nDB.networkNodes[nid]
|
||||
for i, name := range nodes {
|
||||
if name == nodeName {
|
||||
nodes[i] = nodes[len(nodes)-1]
|
||||
nodes = nodes[:len(nodes)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
nDB.networkNodes[nid] = nodes
|
||||
}
|
||||
|
||||
// findCommonnetworks find the networks that both this node and the
|
||||
// passed node have joined.
|
||||
func (nDB *NetworkDB) findCommonNetworks(nodeName string) []string {
|
||||
nDB.RLock()
|
||||
defer nDB.RUnlock()
|
||||
|
||||
var networks []string
|
||||
for nid := range nDB.networks[nDB.config.NodeName] {
|
||||
if _, ok := nDB.networks[nodeName][nid]; ok {
|
||||
networks = append(networks, nid)
|
||||
}
|
||||
}
|
||||
|
||||
return networks
|
||||
}
|
98
vendor/src/github.com/docker/libnetwork/networkdb/watch.go
vendored
Normal file
98
vendor/src/github.com/docker/libnetwork/networkdb/watch.go
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
package networkdb
|
||||
|
||||
import "github.com/docker/go-events"
|
||||
|
||||
type opType uint8
|
||||
|
||||
const (
|
||||
opCreate opType = 1 + iota
|
||||
opUpdate
|
||||
opDelete
|
||||
)
|
||||
|
||||
type event struct {
|
||||
Table string
|
||||
NetworkID string
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// CreateEvent generates a table entry create event to the watchers
|
||||
type CreateEvent event
|
||||
|
||||
// UpdateEvent generates a table entry update event to the watchers
|
||||
type UpdateEvent event
|
||||
|
||||
// DeleteEvent generates a table entry delete event to the watchers
|
||||
type DeleteEvent event
|
||||
|
||||
// Watch creates a watcher with filters for a particular table or
|
||||
// network or key or any combination of the tuple. If any of the
|
||||
// filter is an empty string it acts as a wildcard for that
|
||||
// field. Watch returns a channel of events, where the events will be
|
||||
// sent.
|
||||
func (nDB *NetworkDB) Watch(tname, nid, key string) (chan events.Event, func()) {
|
||||
var matcher events.Matcher
|
||||
|
||||
if tname != "" || nid != "" || key != "" {
|
||||
matcher = events.MatcherFunc(func(ev events.Event) bool {
|
||||
var evt event
|
||||
switch ev := ev.(type) {
|
||||
case CreateEvent:
|
||||
evt = event(ev)
|
||||
case UpdateEvent:
|
||||
evt = event(ev)
|
||||
case DeleteEvent:
|
||||
evt = event(ev)
|
||||
}
|
||||
|
||||
if tname != "" && evt.Table != tname {
|
||||
return false
|
||||
}
|
||||
|
||||
if nid != "" && evt.NetworkID != nid {
|
||||
return false
|
||||
}
|
||||
|
||||
if key != "" && evt.Key != key {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
ch := events.NewChannel(0)
|
||||
sink := events.Sink(events.NewQueue(ch))
|
||||
|
||||
if matcher != nil {
|
||||
sink = events.NewFilter(sink, matcher)
|
||||
}
|
||||
|
||||
nDB.broadcaster.Add(sink)
|
||||
return ch.C, func() {
|
||||
nDB.broadcaster.Remove(sink)
|
||||
ch.Close()
|
||||
sink.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func makeEvent(op opType, tname, nid, key string, value []byte) events.Event {
|
||||
ev := event{
|
||||
Table: tname,
|
||||
NetworkID: nid,
|
||||
Key: key,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
switch op {
|
||||
case opCreate:
|
||||
return CreateEvent(ev)
|
||||
case opUpdate:
|
||||
return UpdateEvent(ev)
|
||||
case opDelete:
|
||||
return DeleteEvent(ev)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
4
vendor/src/github.com/docker/libnetwork/osl/interface_solaris.go
vendored
Normal file
4
vendor/src/github.com/docker/libnetwork/osl/interface_solaris.go
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
package osl
|
||||
|
||||
// IfaceOption is a function option type to set interface options
|
||||
type IfaceOption func()
|
4
vendor/src/github.com/docker/libnetwork/osl/neigh_solaris.go
vendored
Normal file
4
vendor/src/github.com/docker/libnetwork/osl/neigh_solaris.go
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
package osl
|
||||
|
||||
// NeighOption is a function option type to set interface options
|
||||
type NeighOption func()
|
|
@ -142,7 +142,7 @@ func (n *networkNamespace) SetGatewayIPv6(gwv6 net.IP) error {
|
|||
|
||||
err := programGateway(n.nsPath(), gwv6, true)
|
||||
if err == nil {
|
||||
n.SetGatewayIPv6(gwv6)
|
||||
n.setGatewayIPv6(gwv6)
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/resolvconf/dns"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -122,7 +122,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
|
|||
}
|
||||
// if the resulting resolvConf has no more nameservers defined, add appropriate
|
||||
// default DNS servers for IPv4 and (optionally) IPv6
|
||||
if len(GetNameservers(cleanedResolvConf, netutils.IP)) == 0 {
|
||||
if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 {
|
||||
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers : %v", defaultIPv4Dns)
|
||||
dns := defaultIPv4Dns
|
||||
if ipv6Enabled {
|
||||
|
@ -158,11 +158,11 @@ func GetNameservers(resolvConf []byte, kind int) []string {
|
|||
nameservers := []string{}
|
||||
for _, line := range getLines(resolvConf, []byte("#")) {
|
||||
var ns [][]byte
|
||||
if kind == netutils.IP {
|
||||
if kind == types.IP {
|
||||
ns = nsRegexp.FindSubmatch(line)
|
||||
} else if kind == netutils.IPv4 {
|
||||
} else if kind == types.IPv4 {
|
||||
ns = nsIPv4Regexpmatch.FindSubmatch(line)
|
||||
} else if kind == netutils.IPv6 {
|
||||
} else if kind == types.IPv6 {
|
||||
ns = nsIPv6Regexpmatch.FindSubmatch(line)
|
||||
}
|
||||
if len(ns) > 0 {
|
||||
|
@ -177,7 +177,7 @@ func GetNameservers(resolvConf []byte, kind int) []string {
|
|||
// This function's output is intended for net.ParseCIDR
|
||||
func GetNameserversAsCIDR(resolvConf []byte) []string {
|
||||
nameservers := []string{}
|
||||
for _, nameserver := range GetNameservers(resolvConf, netutils.IP) {
|
||||
for _, nameserver := range GetNameservers(resolvConf, types.IP) {
|
||||
nameservers = append(nameservers, nameserver+"/32")
|
||||
}
|
||||
return nameservers
|
||||
|
|
|
@ -9,8 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -47,7 +46,7 @@ const (
|
|||
maxExtDNS = 3 //max number of external servers to try
|
||||
extIOTimeout = 4 * time.Second
|
||||
defaultRespSize = 512
|
||||
maxConcurrent = 50
|
||||
maxConcurrent = 100
|
||||
logInterval = 2 * time.Second
|
||||
maxDNSID = 65536
|
||||
)
|
||||
|
@ -105,8 +104,6 @@ func (r *resolver) SetupFunc() func() {
|
|||
r.err = fmt.Errorf("error in opening name server socket %v", err)
|
||||
return
|
||||
}
|
||||
laddr := r.conn.LocalAddr()
|
||||
_, ipPort, _ := net.SplitHostPort(laddr.String())
|
||||
|
||||
// Listen on a TCP as well
|
||||
tcpaddr := &net.TCPAddr{
|
||||
|
@ -118,21 +115,6 @@ func (r *resolver) SetupFunc() func() {
|
|||
r.err = fmt.Errorf("error in opening name TCP server socket %v", err)
|
||||
return
|
||||
}
|
||||
ltcpaddr := r.tcpListen.Addr()
|
||||
_, tcpPort, _ := net.SplitHostPort(ltcpaddr.String())
|
||||
rules := [][]string{
|
||||
{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", laddr.String()},
|
||||
{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
||||
{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", ltcpaddr.String()},
|
||||
{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
r.err = iptables.RawCombinedOutputNative(rule...)
|
||||
if r.err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
r.err = nil
|
||||
})
|
||||
}
|
||||
|
@ -142,6 +124,11 @@ func (r *resolver) Start() error {
|
|||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
|
||||
if err := r.setupIPTable(); err != nil {
|
||||
return fmt.Errorf("setting up IP table rules failed: %v", err)
|
||||
}
|
||||
|
||||
s := &dns.Server{Handler: r, PacketConn: r.conn}
|
||||
r.server = s
|
||||
go func() {
|
||||
|
@ -240,7 +227,7 @@ func (r *resolver) handleIPQuery(name string, query *dns.Msg, ipType int) (*dns.
|
|||
if len(addr) > 1 {
|
||||
addr = shuffleAddr(addr)
|
||||
}
|
||||
if ipType == netutils.IPv4 {
|
||||
if ipType == types.IPv4 {
|
||||
for _, ip := range addr {
|
||||
rr := new(dns.A)
|
||||
rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
|
||||
|
@ -305,6 +292,7 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|||
extConn net.Conn
|
||||
resp *dns.Msg
|
||||
err error
|
||||
writer dns.ResponseWriter
|
||||
)
|
||||
|
||||
if query == nil || len(query.Question) == 0 {
|
||||
|
@ -312,9 +300,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|||
}
|
||||
name := query.Question[0].Name
|
||||
if query.Question[0].Qtype == dns.TypeA {
|
||||
resp, err = r.handleIPQuery(name, query, netutils.IPv4)
|
||||
resp, err = r.handleIPQuery(name, query, types.IPv4)
|
||||
} else if query.Question[0].Qtype == dns.TypeAAAA {
|
||||
resp, err = r.handleIPQuery(name, query, netutils.IPv6)
|
||||
resp, err = r.handleIPQuery(name, query, types.IPv6)
|
||||
} else if query.Question[0].Qtype == dns.TypePTR {
|
||||
resp, err = r.handlePTRQuery(name, query)
|
||||
}
|
||||
|
@ -342,7 +330,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|||
if resp.Len() > maxSize {
|
||||
truncateResp(resp, maxSize, proto == "tcp")
|
||||
}
|
||||
writer = w
|
||||
} else {
|
||||
queryID := query.Id
|
||||
for i := 0; i < maxExtDNS; i++ {
|
||||
extDNS := &r.extDNSList[i]
|
||||
if extDNS.ipStr == "" {
|
||||
|
@ -388,11 +378,11 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|||
|
||||
// forwardQueryStart stores required context to mux multiple client queries over
|
||||
// one connection; and limits the number of outstanding concurrent queries.
|
||||
if r.forwardQueryStart(w, query) == false {
|
||||
if r.forwardQueryStart(w, query, queryID) == false {
|
||||
old := r.tStamp
|
||||
r.tStamp = time.Now()
|
||||
if r.tStamp.Sub(old) > logInterval {
|
||||
log.Errorf("More than %v concurrent queries from %s", maxConcurrent, w.LocalAddr().String())
|
||||
log.Errorf("More than %v concurrent queries from %s", maxConcurrent, extConn.LocalAddr().String())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
@ -418,32 +408,33 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|||
|
||||
// Retrieves the context for the forwarded query and returns the client connection
|
||||
// to send the reply to
|
||||
w = r.forwardQueryEnd(w, resp)
|
||||
if w == nil {
|
||||
writer = r.forwardQueryEnd(w, resp)
|
||||
if writer == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
resp.Compress = true
|
||||
break
|
||||
}
|
||||
|
||||
if resp == nil || w == nil {
|
||||
if resp == nil || writer == nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = w.WriteMsg(resp)
|
||||
if err != nil {
|
||||
if writer == nil {
|
||||
return
|
||||
}
|
||||
if err = writer.WriteMsg(resp); err != nil {
|
||||
log.Errorf("error writing resolver resp, %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) forwardQueryStart(w dns.ResponseWriter, msg *dns.Msg) bool {
|
||||
func (r *resolver) forwardQueryStart(w dns.ResponseWriter, msg *dns.Msg, queryID uint16) bool {
|
||||
proto := w.LocalAddr().Network()
|
||||
dnsID := uint16(rand.Intn(maxDNSID))
|
||||
|
||||
cc := clientConn{
|
||||
dnsID: msg.Id,
|
||||
dnsID: queryID,
|
||||
respWriter: w,
|
||||
}
|
||||
|
||||
|
@ -462,7 +453,7 @@ func (r *resolver) forwardQueryStart(w dns.ResponseWriter, msg *dns.Msg) bool {
|
|||
for ok := true; ok == true; dnsID = uint16(rand.Intn(maxDNSID)) {
|
||||
_, ok = r.client[dnsID]
|
||||
}
|
||||
log.Debugf("client dns id %v, changed id %v", msg.Id, dnsID)
|
||||
log.Debugf("client dns id %v, changed id %v", queryID, dnsID)
|
||||
r.client[dnsID] = cc
|
||||
msg.Id = dnsID
|
||||
default:
|
||||
|
@ -497,6 +488,7 @@ func (r *resolver) forwardQueryEnd(w dns.ResponseWriter, msg *dns.Msg) dns.Respo
|
|||
log.Debugf("Can't retrieve client context for dns id %v", msg.Id)
|
||||
return nil
|
||||
}
|
||||
log.Debugf("dns msg id %v, client id %v", msg.Id, cc.dnsID)
|
||||
delete(r.client, msg.Id)
|
||||
msg.Id = cc.dnsID
|
||||
w = cc.respWriter
|
||||
|
|
77
vendor/src/github.com/docker/libnetwork/resolver_unix.go
vendored
Normal file
77
vendor/src/github.com/docker/libnetwork/resolver_unix.go
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// +build !windows
|
||||
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/reexec"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
"github.com/vishvananda/netns"
|
||||
)
|
||||
|
||||
func init() {
|
||||
reexec.Register("setup-resolver", reexecSetupResolver)
|
||||
}
|
||||
|
||||
func reexecSetupResolver() {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
if len(os.Args) < 4 {
|
||||
log.Error("invalid number of arguments..")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, ipPort, _ := net.SplitHostPort(os.Args[2])
|
||||
_, tcpPort, _ := net.SplitHostPort(os.Args[3])
|
||||
rules := [][]string{
|
||||
{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "udp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[2]},
|
||||
{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "udp", "--sport", ipPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
||||
{"-t", "nat", "-A", "OUTPUT", "-d", resolverIP, "-p", "tcp", "--dport", dnsPort, "-j", "DNAT", "--to-destination", os.Args[3]},
|
||||
{"-t", "nat", "-A", "POSTROUTING", "-s", resolverIP, "-p", "tcp", "--sport", tcpPort, "-j", "SNAT", "--to-source", ":" + dnsPort},
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(os.Args[1], os.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
log.Errorf("failed get network namespace %q: %v", os.Args[1], err)
|
||||
os.Exit(2)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
nsFD := f.Fd()
|
||||
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
|
||||
log.Errorf("setting into container net ns %v failed, %v", os.Args[1], err)
|
||||
os.Exit(3)
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
if iptables.RawCombinedOutputNative(rule...) != nil {
|
||||
log.Errorf("setting up rule failed, %v", rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *resolver) setupIPTable() error {
|
||||
if r.err != nil {
|
||||
return r.err
|
||||
}
|
||||
laddr := r.conn.LocalAddr().String()
|
||||
ltcpaddr := r.tcpListen.Addr().String()
|
||||
|
||||
cmd := &exec.Cmd{
|
||||
Path: reexec.Self(),
|
||||
Args: append([]string{"setup-resolver"}, r.sb.Key(), laddr, ltcpaddr),
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("reexec failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
7
vendor/src/github.com/docker/libnetwork/resolver_windows.go
vendored
Normal file
7
vendor/src/github.com/docker/libnetwork/resolver_windows.go
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build windows
|
||||
|
||||
package libnetwork
|
||||
|
||||
func (r *resolver) setupIPTable() error {
|
||||
return nil
|
||||
}
|
|
@ -12,7 +12,6 @@ import (
|
|||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/etchosts"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -406,7 +405,7 @@ func (sb *sandbox) ResolveIP(ip string) string {
|
|||
for _, ep := range sb.getConnectedEndpoints() {
|
||||
n := ep.getNetwork()
|
||||
|
||||
sr, ok := n.getController().svcDb[n.ID()]
|
||||
sr, ok := n.getController().svcRecords[n.ID()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
@ -436,6 +435,7 @@ func (sb *sandbox) ResolveName(name string, ipType int) ([]net.IP, bool) {
|
|||
// {a.b in network c.d},
|
||||
// {a in network b.c.d},
|
||||
|
||||
log.Debugf("Name To resolve: %v", name)
|
||||
name = strings.TrimSuffix(name, ".")
|
||||
reqName := []string{name}
|
||||
networkName := []string{""}
|
||||
|
@ -456,7 +456,6 @@ func (sb *sandbox) ResolveName(name string, ipType int) ([]net.IP, bool) {
|
|||
|
||||
epList := sb.getConnectedEndpoints()
|
||||
for i := 0; i < len(reqName); i++ {
|
||||
log.Debugf("To resolve: %v in %v", reqName[i], networkName[i])
|
||||
|
||||
// First check for local container alias
|
||||
ip, ipv6Miss := sb.resolveName(reqName[i], networkName[i], epList, true, ipType)
|
||||
|
@ -513,7 +512,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
|
|||
ep.Unlock()
|
||||
}
|
||||
|
||||
sr, ok := n.getController().svcDb[n.ID()]
|
||||
sr, ok := n.getController().svcRecords[n.ID()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
@ -522,7 +521,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
|
|||
n.Lock()
|
||||
ip, ok = sr.svcMap[name]
|
||||
|
||||
if ipType == netutils.IPv6 {
|
||||
if ipType == types.IPv6 {
|
||||
// If the name resolved to v4 address then its a valid name in
|
||||
// the docker network domain. If the network is not v6 enabled
|
||||
// set ipv6Miss to filter the DNS query from going to external
|
||||
|
@ -972,6 +971,14 @@ func (eh epHeap) Less(i, j int) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
if epi.getNetwork().Internal() {
|
||||
return false
|
||||
}
|
||||
|
||||
if epj.getNetwork().Internal() {
|
||||
return true
|
||||
}
|
||||
|
||||
if ci != nil {
|
||||
cip, ok = ci.epPriority[eh[i].ID()]
|
||||
if !ok {
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/etchosts"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -91,6 +90,10 @@ func (sb *sandbox) buildHostsFile() error {
|
|||
func (sb *sandbox) updateHostsFile(ifaceIP string) error {
|
||||
var mhost string
|
||||
|
||||
if ifaceIP == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sb.config.originHostsPath != "" {
|
||||
return nil
|
||||
}
|
||||
|
@ -166,7 +169,7 @@ func (sb *sandbox) setupDNS() error {
|
|||
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
|
||||
var (
|
||||
err error
|
||||
dnsList = resolvconf.GetNameservers(currRC.Content, netutils.IP)
|
||||
dnsList = resolvconf.GetNameservers(currRC.Content, types.IP)
|
||||
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
|
||||
dnsOptionsList = resolvconf.GetOptions(currRC.Content)
|
||||
)
|
||||
|
@ -275,7 +278,7 @@ func (sb *sandbox) rebuildDNS() error {
|
|||
|
||||
// localhost entries have already been filtered out from the list
|
||||
// retain only the v4 servers in sb for forwarding the DNS queries
|
||||
sb.extDNS = resolvconf.GetNameservers(currRC.Content, netutils.IPv4)
|
||||
sb.extDNS = resolvconf.GetNameservers(currRC.Content, types.IPv4)
|
||||
|
||||
var (
|
||||
dnsList = []string{sb.resolver.NameServer()}
|
||||
|
@ -284,7 +287,7 @@ func (sb *sandbox) rebuildDNS() error {
|
|||
)
|
||||
|
||||
// external v6 DNS servers has to be listed in resolv.conf
|
||||
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, netutils.IPv6)...)
|
||||
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, types.IPv6)...)
|
||||
|
||||
// Resolver returns the options in the format resolv.conf expects
|
||||
dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)
|
||||
|
|
45
vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go
vendored
Normal file
45
vendor/src/github.com/docker/libnetwork/sandbox_externalkey_solaris.go
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// +build solaris
|
||||
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// processSetKeyReexec is a private function that must be called only on an reexec path
|
||||
// It expects 3 args { [0] = "libnetwork-setkey", [1] = <container-id>, [2] = <controller-id> }
|
||||
// It also expects libcontainer.State as a json string in <stdin>
|
||||
// Refer to https://github.com/opencontainers/runc/pull/160/ for more information
|
||||
func processSetKeyReexec() {
|
||||
}
|
||||
|
||||
// SetExternalKey provides a convenient way to set an External key to a sandbox
|
||||
func SetExternalKey(controllerID string, containerID string, key string) error {
|
||||
return types.NotImplementedErrorf("SetExternalKey isn't supported on non linux systems")
|
||||
}
|
||||
|
||||
func sendKey(c net.Conn, data setKeyData) error {
|
||||
return types.NotImplementedErrorf("sendKey isn't supported on non linux systems")
|
||||
}
|
||||
|
||||
func processReturn(r io.Reader) error {
|
||||
return types.NotImplementedErrorf("processReturn isn't supported on non linux systems")
|
||||
}
|
||||
|
||||
// no-op on non linux systems
|
||||
func (c *controller) startExternalKeyListener() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) acceptClientConnections(sock string, l net.Listener) {
|
||||
}
|
||||
|
||||
func (c *controller) processExternalKey(conn net.Conn) error {
|
||||
return types.NotImplementedErrorf("processExternalKey isn't supported on non linux systems")
|
||||
}
|
||||
|
||||
func (c *controller) stopExternalKeyListener() {
|
||||
}
|
|
@ -213,7 +213,7 @@ func (c *controller) sandboxCleanup() {
|
|||
var ep *endpoint
|
||||
if err != nil {
|
||||
logrus.Errorf("getNetworkFromStore for nid %s failed while trying to build sandbox for cleanup: %v", eps.Nid, err)
|
||||
n = &network{id: eps.Nid, ctrlr: c, drvOnce: &sync.Once{}}
|
||||
n = &network{id: eps.Nid, ctrlr: c, drvOnce: &sync.Once{}, persist: true}
|
||||
ep = &endpoint{id: eps.Eid, network: n, sandboxID: sbs.ID}
|
||||
} else {
|
||||
ep, err = n.getEndpointFromStore(eps.Eid)
|
||||
|
|
80
vendor/src/github.com/docker/libnetwork/service.go
vendored
Normal file
80
vendor/src/github.com/docker/libnetwork/service.go
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
package libnetwork
|
||||
|
||||
import "net"
|
||||
|
||||
type service struct {
|
||||
name string
|
||||
id string
|
||||
backEnds map[string]map[string]net.IP
|
||||
}
|
||||
|
||||
func newService(name string, id string) *service {
|
||||
return &service{
|
||||
name: name,
|
||||
id: id,
|
||||
backEnds: make(map[string]map[string]net.IP),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) addServiceBinding(name, sid, nid, eid string, ip net.IP) error {
|
||||
var s *service
|
||||
|
||||
n, err := c.NetworkByID(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
s, ok := c.serviceBindings[sid]
|
||||
if !ok {
|
||||
s = newService(name, sid)
|
||||
}
|
||||
|
||||
netBackEnds, ok := s.backEnds[nid]
|
||||
if !ok {
|
||||
netBackEnds = make(map[string]net.IP)
|
||||
s.backEnds[nid] = netBackEnds
|
||||
}
|
||||
|
||||
netBackEnds[eid] = ip
|
||||
c.serviceBindings[sid] = s
|
||||
c.Unlock()
|
||||
|
||||
n.(*network).addSvcRecords(name, ip, nil, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) rmServiceBinding(name, sid, nid, eid string, ip net.IP) error {
|
||||
n, err := c.NetworkByID(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
s, ok := c.serviceBindings[sid]
|
||||
if !ok {
|
||||
c.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
netBackEnds, ok := s.backEnds[nid]
|
||||
if !ok {
|
||||
c.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
delete(netBackEnds, eid)
|
||||
|
||||
if len(netBackEnds) == 0 {
|
||||
delete(s.backEnds, nid)
|
||||
}
|
||||
|
||||
if len(s.backEnds) == 0 {
|
||||
delete(c.serviceBindings, sid)
|
||||
}
|
||||
c.Unlock()
|
||||
|
||||
n.(*network).deleteSvcRecords(name, ip, nil, false)
|
||||
|
||||
return err
|
||||
}
|
21
vendor/src/github.com/docker/libnetwork/store.go
vendored
21
vendor/src/github.com/docker/libnetwork/store.go
vendored
|
@ -4,9 +4,20 @@ import (
|
|||
"fmt"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/docker/libkv/store/zookeeper"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
)
|
||||
|
||||
func registerKVStores() {
|
||||
consul.Register()
|
||||
zookeeper.Register()
|
||||
etcd.Register()
|
||||
boltdb.Register()
|
||||
}
|
||||
|
||||
func (c *controller) initScopedStore(scope string, scfg *datastore.ScopeCfg) error {
|
||||
store, err := datastore.NewDataStore(scope, scfg)
|
||||
if err != nil {
|
||||
|
@ -20,6 +31,8 @@ func (c *controller) initScopedStore(scope string, scfg *datastore.ScopeCfg) err
|
|||
}
|
||||
|
||||
func (c *controller) initStores() error {
|
||||
registerKVStores()
|
||||
|
||||
c.Lock()
|
||||
if c.cfg == nil {
|
||||
c.Unlock()
|
||||
|
@ -208,8 +221,7 @@ func (n *network) getEndpointsFromStore() ([]*endpoint, error) {
|
|||
func (c *controller) updateToStore(kvObject datastore.KVObject) error {
|
||||
cs := c.getStore(kvObject.DataScope())
|
||||
if cs == nil {
|
||||
log.Warnf("datastore for scope %s not initialized. kv object %s is not added to the store", kvObject.DataScope(), datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
return fmt.Errorf("datastore for scope %q is not initialized ", kvObject.DataScope())
|
||||
}
|
||||
|
||||
if err := cs.PutObjectAtomic(kvObject); err != nil {
|
||||
|
@ -225,8 +237,7 @@ func (c *controller) updateToStore(kvObject datastore.KVObject) error {
|
|||
func (c *controller) deleteFromStore(kvObject datastore.KVObject) error {
|
||||
cs := c.getStore(kvObject.DataScope())
|
||||
if cs == nil {
|
||||
log.Debugf("datastore for scope %s not initialized. kv object %s is not deleted from datastore", kvObject.DataScope(), datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
return fmt.Errorf("datastore for scope %q is not initialized ", kvObject.DataScope())
|
||||
}
|
||||
|
||||
retry:
|
||||
|
@ -407,7 +418,7 @@ func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoi
|
|||
|
||||
// This is the last container going away for the network. Destroy
|
||||
// this network's svc db entry
|
||||
delete(c.svcDb, ep.getNetwork().ID())
|
||||
delete(c.svcRecords, ep.getNetwork().ID())
|
||||
|
||||
delete(nmap, ep.getNetwork().ID())
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue