Vendoring in libnetwork ipam support
* Pluggable IPAM driver support * Performance and Scalability enhancements * Libkv fixes in multiple backend stores Signed-off-by: Madhu Venugopal <madhu@docker.com>
This commit is contained in:
parent
ed9434c5bb
commit
2e3113aeef
66 changed files with 5848 additions and 1982 deletions
|
@ -20,12 +20,12 @@ clone git github.com/tchap/go-patricia v2.1.0
|
|||
clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git
|
||||
|
||||
#get libnetwork packages
|
||||
clone git github.com/docker/libnetwork c3a9e0d8d0c53f3db251620e5f48470e267f292b
|
||||
clone git github.com/docker/libnetwork 0521fe53fc3e7d4e7c2e463800f36662c9169f20
|
||||
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 ea7ff6ae76485ab93ac36799d3e13b1905787ffe
|
||||
clone git github.com/docker/libkv 958cd316db2bc916466bdcc632a3188d62ce6e87
|
||||
clone git github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
|
||||
clone git github.com/vishvananda/netlink 4b5dce31de6d42af5bb9811c6d265472199e0fec
|
||||
clone git github.com/BurntSushi/toml f706d00e3de6abe700c994cdd545a1a4915af060
|
||||
|
|
7
vendor/src/github.com/docker/libkv/README.md
vendored
7
vendor/src/github.com/docker/libkv/README.md
vendored
|
@ -68,6 +68,13 @@ func main() {
|
|||
|
||||
You can find other usage examples for `libkv` under the `docker/swarm` or `docker/libnetwork` repositories.
|
||||
|
||||
## Supported versions
|
||||
|
||||
`libkv` supports:
|
||||
- Consul version >= `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 version >= `2.0` because it uses the `2.0.0` branch of the `coreos/go-etcd` client, this might change in the future as the support for `APIv3` comes along.
|
||||
- Zookeeper version >= `3.4.5`. Although this might work with previous version but this remains untested as of now.
|
||||
|
||||
## TLS
|
||||
|
||||
The etcd backend supports etcd servers that require TLS Client Authentication. Zookeeper and Consul support are planned. This feature is somewhat experimental and the store.ClientTLSConfig struct may change to accommodate the additional backends.
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
"github.com/docker/libkv"
|
||||
|
@ -25,15 +27,29 @@ var (
|
|||
ErrBoltAPIUnsupported = errors.New("API not supported by BoltDB backend")
|
||||
)
|
||||
|
||||
const (
|
||||
filePerm os.FileMode = 0644
|
||||
)
|
||||
|
||||
//BoltDB type implements the Store interface
|
||||
type BoltDB struct {
|
||||
client *bolt.DB
|
||||
boltBucket []byte
|
||||
dbIndex uint64
|
||||
path string
|
||||
timeout time.Duration
|
||||
// By default libkv opens and closes the bolt DB connection for every
|
||||
// get/put operation. This allows multiple apps to use a Bolt DB at the
|
||||
// same time.
|
||||
// PersistConnection flag provides an option to override ths behavior.
|
||||
// ie: open the connection in New and use it till Close is called.
|
||||
PersistConnection bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
const (
|
||||
libkvmetadatalen = 8
|
||||
transientTimeout = time.Duration(10) * time.Second
|
||||
)
|
||||
|
||||
// Register registers boltdb to libkv
|
||||
|
@ -43,6 +59,12 @@ func Register() {
|
|||
|
||||
// New opens a new BoltDB connection to the specified path and bucket
|
||||
func New(endpoints []string, options *store.Config) (store.Store, error) {
|
||||
var (
|
||||
db *bolt.DB
|
||||
err error
|
||||
boltOptions *bolt.Options
|
||||
)
|
||||
|
||||
if len(endpoints) > 1 {
|
||||
return nil, ErrMultipleEndpointsUnsupported
|
||||
}
|
||||
|
@ -52,39 +74,81 @@ func New(endpoints []string, options *store.Config) (store.Store, error) {
|
|||
}
|
||||
|
||||
dir, _ := filepath.Split(endpoints[0])
|
||||
if err := os.MkdirAll(dir, 0750); err != nil {
|
||||
if err = os.MkdirAll(dir, 0750); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var boltOptions *bolt.Options
|
||||
if options != nil {
|
||||
if options.PersistConnection {
|
||||
boltOptions = &bolt.Options{Timeout: options.ConnectionTimeout}
|
||||
}
|
||||
db, err := bolt.Open(endpoints[0], 0644, boltOptions)
|
||||
db, err = bolt.Open(endpoints[0], filePerm, boltOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
b := &BoltDB{}
|
||||
b := &BoltDB{
|
||||
client: db,
|
||||
path: endpoints[0],
|
||||
boltBucket: []byte(options.Bucket),
|
||||
timeout: transientTimeout,
|
||||
PersistConnection: options.PersistConnection,
|
||||
}
|
||||
|
||||
b.client = db
|
||||
b.boltBucket = []byte(options.Bucket)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (b *BoltDB) reset() {
|
||||
b.path = ""
|
||||
b.boltBucket = []byte{}
|
||||
}
|
||||
|
||||
func (b *BoltDB) getDBhandle() (*bolt.DB, error) {
|
||||
var (
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
if !b.PersistConnection {
|
||||
boltOptions := &bolt.Options{Timeout: b.timeout}
|
||||
if db, err = bolt.Open(b.path, filePerm, boltOptions); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.client = db
|
||||
}
|
||||
|
||||
return b.client, nil
|
||||
}
|
||||
|
||||
func (b *BoltDB) releaseDBhandle() {
|
||||
if !b.PersistConnection {
|
||||
b.client.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value at "key". BoltDB doesn't provide an inbuilt last modified index with every kv pair. Its implemented by
|
||||
// by a atomic counter maintained by the libkv and appened to the value passed by the client.
|
||||
func (b *BoltDB) Get(key string) (*store.KVPair, error) {
|
||||
var val []byte
|
||||
var (
|
||||
val []byte
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
db := b.client
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err = db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return (ErrBoltBucketNotFound)
|
||||
return ErrBoltBucketNotFound
|
||||
}
|
||||
|
||||
val = bucket.Get([]byte(key))
|
||||
v := bucket.Get([]byte(key))
|
||||
val = make([]byte, len(v))
|
||||
copy(val, v)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
@ -104,11 +168,22 @@ func (b *BoltDB) Get(key string) (*store.KVPair, error) {
|
|||
|
||||
//Put the key, value pair. index number metadata is prepended to the value
|
||||
func (b *BoltDB) Put(key string, value []byte, opts *store.WriteOptions) error {
|
||||
var dbIndex uint64
|
||||
db := b.client
|
||||
var (
|
||||
dbIndex uint64
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
dbval := make([]byte, libkvmetadatalen)
|
||||
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket, err := tx.CreateBucketIfNotExists(b.boltBucket)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -129,12 +204,22 @@ func (b *BoltDB) Put(key string, value []byte, opts *store.WriteOptions) error {
|
|||
|
||||
//Delete the value for the given key.
|
||||
func (b *BoltDB) Delete(key string) error {
|
||||
db := b.client
|
||||
var (
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return (ErrBoltBucketNotFound)
|
||||
return ErrBoltBucketNotFound
|
||||
}
|
||||
err := bucket.Delete([]byte(key))
|
||||
return err
|
||||
|
@ -144,13 +229,23 @@ func (b *BoltDB) Delete(key string) error {
|
|||
|
||||
// Exists checks if the key exists inside the store
|
||||
func (b *BoltDB) Exists(key string) (bool, error) {
|
||||
var val []byte
|
||||
var (
|
||||
val []byte
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
db := b.client
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err = db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return (ErrBoltBucketNotFound)
|
||||
return ErrBoltBucketNotFound
|
||||
}
|
||||
|
||||
val = bucket.Get([]byte(key))
|
||||
|
@ -166,22 +261,35 @@ func (b *BoltDB) Exists(key string) (bool, error) {
|
|||
|
||||
// List returns the range of keys starting with the passed in prefix
|
||||
func (b *BoltDB) List(keyPrefix string) ([]*store.KVPair, error) {
|
||||
var (
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
kv := []*store.KVPair{}
|
||||
|
||||
db := b.client
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err = db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return (ErrBoltBucketNotFound)
|
||||
return ErrBoltBucketNotFound
|
||||
}
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
prefix := []byte(keyPrefix)
|
||||
|
||||
for key, val := cursor.Seek(prefix); bytes.HasPrefix(key, prefix); key, val = cursor.Next() {
|
||||
for key, v := cursor.Seek(prefix); bytes.HasPrefix(key, prefix); key, v = cursor.Next() {
|
||||
|
||||
dbIndex := binary.LittleEndian.Uint64(val[:libkvmetadatalen])
|
||||
val = val[libkvmetadatalen:]
|
||||
dbIndex := binary.LittleEndian.Uint64(v[:libkvmetadatalen])
|
||||
v = v[libkvmetadatalen:]
|
||||
val := make([]byte, len(v))
|
||||
copy(val, v)
|
||||
|
||||
kv = append(kv, &store.KVPair{
|
||||
Key: string(key),
|
||||
|
@ -201,22 +309,30 @@ func (b *BoltDB) List(keyPrefix string) ([]*store.KVPair, error) {
|
|||
// has not been modified in the meantime, throws an
|
||||
// error if this is the case
|
||||
func (b *BoltDB) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
|
||||
var val []byte
|
||||
var dbIndex uint64
|
||||
var (
|
||||
val []byte
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
if previous == nil {
|
||||
return false, store.ErrPreviousNotSpecified
|
||||
}
|
||||
db := b.client
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return ErrBoltBucketNotFound
|
||||
}
|
||||
|
||||
val = bucket.Get([]byte(key))
|
||||
dbIndex = binary.LittleEndian.Uint64(val[:libkvmetadatalen])
|
||||
dbIndex := binary.LittleEndian.Uint64(val[:libkvmetadatalen])
|
||||
if dbIndex != previous.LastIndex {
|
||||
return store.ErrKeyModified
|
||||
}
|
||||
|
@ -232,13 +348,23 @@ func (b *BoltDB) AtomicDelete(key string, previous *store.KVPair) (bool, error)
|
|||
// AtomicPut puts a value at "key" if the key has not been
|
||||
// modified since the last Put, throws an error if this is the case
|
||||
func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
|
||||
var val []byte
|
||||
var dbIndex uint64
|
||||
var (
|
||||
val []byte
|
||||
dbIndex uint64
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
dbval := make([]byte, libkvmetadatalen)
|
||||
|
||||
db := b.client
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
var err error
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
|
@ -285,18 +411,35 @@ func (b *BoltDB) AtomicPut(key string, value []byte, previous *store.KVPair, opt
|
|||
|
||||
// Close the db connection to the BoltDB
|
||||
func (b *BoltDB) Close() {
|
||||
db := b.client
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
db.Close()
|
||||
if !b.PersistConnection {
|
||||
b.reset()
|
||||
} else {
|
||||
b.client.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteTree deletes a range of keys with a given prefix
|
||||
func (b *BoltDB) DeleteTree(keyPrefix string) error {
|
||||
db := b.client
|
||||
err := db.Update(func(tx *bolt.Tx) error {
|
||||
var (
|
||||
db *bolt.DB
|
||||
err error
|
||||
)
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
if db, err = b.getDBhandle(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer b.releaseDBhandle()
|
||||
|
||||
err = db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket(b.boltBucket)
|
||||
if bucket == nil {
|
||||
return (ErrBoltBucketNotFound)
|
||||
return ErrBoltBucketNotFound
|
||||
}
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
|
|
|
@ -36,6 +36,7 @@ type Consul struct {
|
|||
|
||||
type consulLock struct {
|
||||
lock *api.Lock
|
||||
renewCh chan struct{}
|
||||
}
|
||||
|
||||
// Register registers consul to libkv
|
||||
|
@ -87,7 +88,7 @@ func (s *Consul) setTLS(tls *tls.Config) {
|
|||
s.config.Scheme = "https"
|
||||
}
|
||||
|
||||
// SetTimeout sets the timout for connecting to Consul
|
||||
// SetTimeout sets the timeout for connecting to Consul
|
||||
func (s *Consul) setTimeout(time time.Duration) {
|
||||
s.config.WaitTime = time
|
||||
}
|
||||
|
@ -360,32 +361,63 @@ func (s *Consul) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*
|
|||
// NewLock returns a handle to a lock struct which can
|
||||
// be used to provide mutual exclusion on a key
|
||||
func (s *Consul) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
|
||||
consulOpts := &api.LockOptions{
|
||||
lockOpts := &api.LockOptions{
|
||||
Key: s.normalize(key),
|
||||
}
|
||||
|
||||
lock := &consulLock{}
|
||||
|
||||
if options != nil {
|
||||
consulOpts.Value = options.Value
|
||||
// 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
|
||||
}
|
||||
|
||||
l, err := s.client.LockOpts(consulOpts)
|
||||
// Create the key session
|
||||
session, _, err := s.client.Session().Create(entry, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &consulLock{lock: l}, nil
|
||||
// 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
|
||||
}
|
||||
|
||||
// Set optional value on Lock
|
||||
if options.Value != nil {
|
||||
lockOpts.Value = options.Value
|
||||
}
|
||||
}
|
||||
|
||||
l, err := s.client.LockOpts(lockOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lock.lock = l
|
||||
return lock, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
func (l *consulLock) Lock() (<-chan struct{}, error) {
|
||||
return l.lock.Lock(nil)
|
||||
func (l *consulLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) {
|
||||
return l.lock.Lock(stopChan)
|
||||
}
|
||||
|
||||
// Unlock the "key". Calling unlock while
|
||||
// not holding the lock will throw an error
|
||||
func (l *consulLock) Unlock() error {
|
||||
if l.renewCh != nil {
|
||||
close(l.renewCh)
|
||||
}
|
||||
return l.lock.Unlock()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package etcd
|
|||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -12,6 +13,13 @@ import (
|
|||
"github.com/docker/libkv/store"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrAbortTryLock is thrown when a user stops trying to seek the lock
|
||||
// by sending a signal to the stop chan, this is used to verify if the
|
||||
// operation succeeded
|
||||
ErrAbortTryLock = errors.New("lock operation aborted")
|
||||
)
|
||||
|
||||
// Etcd is the receiver type for the
|
||||
// Store interface
|
||||
type Etcd struct {
|
||||
|
@ -21,6 +29,7 @@ type Etcd struct {
|
|||
type etcdLock struct {
|
||||
client *etcd.Client
|
||||
stopLock chan struct{}
|
||||
stopRenew chan struct{}
|
||||
key string
|
||||
value string
|
||||
last *etcd.Response
|
||||
|
@ -395,6 +404,7 @@ func (s *Etcd) DeleteTree(directory string) error {
|
|||
func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locker, err error) {
|
||||
var value string
|
||||
ttl := uint64(time.Duration(defaultLockTTL).Seconds())
|
||||
renewCh := make(chan struct{})
|
||||
|
||||
// Apply options on Lock
|
||||
if options != nil {
|
||||
|
@ -404,11 +414,15 @@ func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locke
|
|||
if options.TTL != 0 {
|
||||
ttl = uint64(options.TTL.Seconds())
|
||||
}
|
||||
if options.RenewLock != nil {
|
||||
renewCh = options.RenewLock
|
||||
}
|
||||
}
|
||||
|
||||
// Create lock object
|
||||
lock = &etcdLock{
|
||||
client: s.client,
|
||||
stopRenew: renewCh,
|
||||
key: key,
|
||||
value: value,
|
||||
ttl: ttl,
|
||||
|
@ -420,13 +434,13 @@ func (s *Etcd) NewLock(key string, options *store.LockOptions) (lock store.Locke
|
|||
// 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
|
||||
func (l *etcdLock) Lock() (<-chan struct{}, error) {
|
||||
func (l *etcdLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) {
|
||||
|
||||
key := store.Normalize(l.key)
|
||||
|
||||
// Lock holder channels
|
||||
// Lock holder channel
|
||||
lockHeld := make(chan struct{})
|
||||
stopLocking := make(chan struct{})
|
||||
stopLocking := l.stopRenew
|
||||
|
||||
var lastIndex uint64
|
||||
|
||||
|
@ -454,7 +468,18 @@ func (l *etcdLock) Lock() (<-chan struct{}, error) {
|
|||
// Seeker section
|
||||
chW := make(chan *etcd.Response)
|
||||
chWStop := make(chan bool)
|
||||
l.waitLock(key, chW, chWStop)
|
||||
free := make(chan bool)
|
||||
|
||||
go l.waitLock(key, chW, chWStop, free)
|
||||
|
||||
// Wait for the key to be available or for
|
||||
// a signal to stop trying to lock the key
|
||||
select {
|
||||
case _ = <-free:
|
||||
break
|
||||
case _ = <-stopChan:
|
||||
return nil, ErrAbortTryLock
|
||||
}
|
||||
|
||||
// Delete or Expire event occured
|
||||
// Retry
|
||||
|
@ -467,10 +492,10 @@ func (l *etcdLock) Lock() (<-chan struct{}, error) {
|
|||
// Hold the lock as long as we can
|
||||
// Updates the key ttl periodically until we receive
|
||||
// an explicit stop signal from the Unlock method
|
||||
func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking chan struct{}) {
|
||||
func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking <-chan struct{}) {
|
||||
defer close(lockHeld)
|
||||
|
||||
update := time.NewTicker(defaultUpdateTime)
|
||||
update := time.NewTicker(time.Duration(l.ttl) * time.Second / 3)
|
||||
defer update.Stop()
|
||||
|
||||
var err error
|
||||
|
@ -490,11 +515,12 @@ func (l *etcdLock) holdLock(key string, lockHeld chan struct{}, stopLocking chan
|
|||
}
|
||||
|
||||
// WaitLock simply waits for the key to be available for creation
|
||||
func (l *etcdLock) waitLock(key string, eventCh chan *etcd.Response, stopWatchCh chan bool) {
|
||||
func (l *etcdLock) waitLock(key string, eventCh chan *etcd.Response, stopWatchCh chan bool, free chan<- bool) {
|
||||
go l.client.Watch(key, 0, false, eventCh, stopWatchCh)
|
||||
|
||||
for event := range eventCh {
|
||||
if event.Action == "delete" || event.Action == "expire" {
|
||||
return
|
||||
free <- true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ type Config struct {
|
|||
TLS *tls.Config
|
||||
ConnectionTimeout time.Duration
|
||||
Bucket string
|
||||
PersistConnection bool
|
||||
}
|
||||
|
||||
// ClientTLSConfig contains data for a Client TLS configuration in the form
|
||||
|
@ -115,11 +116,12 @@ type WriteOptions struct {
|
|||
type LockOptions struct {
|
||||
Value []byte // Optional, value to associate with the lock
|
||||
TTL time.Duration // Optional, expiration ttl associated with the lock
|
||||
RenewLock chan struct{} // Optional, chan used to control and stop the session ttl renewal for the lock
|
||||
}
|
||||
|
||||
// Locker provides locking mechanism on top of the store.
|
||||
// Similar to `sync.Lock` except it may return errors.
|
||||
type Locker interface {
|
||||
Lock() (<-chan struct{}, error)
|
||||
Lock(stopChan chan struct{}) (<-chan struct{}, error)
|
||||
Unlock() error
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// SOH control character
|
||||
SOH = "\x01"
|
||||
|
||||
defaultTimeout = 10 * time.Second
|
||||
)
|
||||
|
||||
|
@ -72,6 +75,12 @@ func (s *Zookeeper) Get(key string) (pair *store.KVPair, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// FIXME handle very rare cases where Get returns the
|
||||
// SOH control character instead of the actual value
|
||||
if string(resp) == SOH {
|
||||
return s.Get(store.Normalize(key))
|
||||
}
|
||||
|
||||
pair = &store.KVPair{
|
||||
Key: key,
|
||||
Value: resp,
|
||||
|
@ -301,7 +310,7 @@ func (s *Zookeeper) AtomicPut(key string, value []byte, previous *store.KVPair,
|
|||
// Zookeeper will complain if the directory doesn't exist.
|
||||
if err == zk.ErrNoNode {
|
||||
// Create the directory
|
||||
parts := store.SplitKey(key)
|
||||
parts := store.SplitKey(strings.TrimSuffix(key, "/"))
|
||||
parts = parts[:len(parts)-1]
|
||||
if err = s.createFullPath(parts, false); err != nil {
|
||||
// Failed to create the directory.
|
||||
|
@ -371,7 +380,7 @@ func (s *Zookeeper) NewLock(key string, options *store.LockOptions) (lock store.
|
|||
// 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
|
||||
func (l *zookeeperLock) Lock() (<-chan struct{}, error) {
|
||||
func (l *zookeeperLock) Lock(stopChan chan struct{}) (<-chan struct{}, error) {
|
||||
err := l.lock.Lock()
|
||||
|
||||
if err == nil {
|
||||
|
|
|
@ -29,7 +29,8 @@ cmd/dnet/dnet
|
|||
*.tmp
|
||||
*.coverprofile
|
||||
|
||||
# IDE files
|
||||
# IDE files and folders
|
||||
.project
|
||||
.settings/
|
||||
|
||||
libnetwork-build.created
|
||||
|
|
|
@ -5,6 +5,7 @@ package bitseq
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
|
@ -16,10 +17,10 @@ import (
|
|||
// If needed we can think of making these configurable
|
||||
const (
|
||||
blockLen = uint32(32)
|
||||
blockBytes = blockLen / 8
|
||||
blockBytes = uint64(blockLen / 8)
|
||||
blockMAX = uint32(1<<blockLen - 1)
|
||||
blockFirstBit = uint32(1) << (blockLen - 1)
|
||||
invalidPos = blockMAX
|
||||
invalidPos = uint64(0xFFFFFFFFFFFFFFFF)
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -28,8 +29,8 @@ var (
|
|||
|
||||
// Handle contains the sequece representing the bitmask and its identifier
|
||||
type Handle struct {
|
||||
bits uint32
|
||||
unselected uint32
|
||||
bits uint64
|
||||
unselected uint64
|
||||
head *sequence
|
||||
app string
|
||||
id string
|
||||
|
@ -40,7 +41,7 @@ type Handle struct {
|
|||
}
|
||||
|
||||
// NewHandle returns a thread-safe instance of the bitmask handler
|
||||
func NewHandle(app string, ds datastore.DataStore, id string, numElements uint32) (*Handle, error) {
|
||||
func NewHandle(app string, ds datastore.DataStore, id string, numElements uint64) (*Handle, error) {
|
||||
h := &Handle{
|
||||
app: app,
|
||||
id: id,
|
||||
|
@ -57,21 +58,25 @@ func NewHandle(app string, ds datastore.DataStore, id string, numElements uint32
|
|||
return h, nil
|
||||
}
|
||||
|
||||
// Register for status changes
|
||||
h.watchForChanges()
|
||||
|
||||
// Get the initial status from the ds if present.
|
||||
if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the handle is not in store, write it.
|
||||
if !h.Exists() {
|
||||
if err := h.writeToStore(); err != nil {
|
||||
return nil, fmt.Errorf("failed to write bitsequence to store: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// sequence represents a recurring sequence of 32 bits long bitmasks
|
||||
type sequence struct {
|
||||
block uint32 // block is a symbol representing 4 byte long allocation bitmask
|
||||
count uint32 // number of consecutive blocks (symbols)
|
||||
count uint64 // number of consecutive blocks (symbols)
|
||||
next *sequence // next sequence
|
||||
}
|
||||
|
||||
|
@ -87,7 +92,7 @@ func (s *sequence) toString() string {
|
|||
}
|
||||
|
||||
// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence
|
||||
func (s *sequence) getAvailableBit(from uint32) (uint32, uint32, error) {
|
||||
func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
|
||||
if s.block == blockMAX || s.count == 0 {
|
||||
return invalidPos, invalidPos, errNoBitAvailable
|
||||
}
|
||||
|
@ -140,9 +145,9 @@ func (s *sequence) toByteArray() ([]byte, error) {
|
|||
|
||||
p := s
|
||||
for p != nil {
|
||||
b := make([]byte, 8)
|
||||
b := make([]byte, 12)
|
||||
binary.BigEndian.PutUint32(b[0:], p.block)
|
||||
binary.BigEndian.PutUint32(b[4:], p.count)
|
||||
binary.BigEndian.PutUint64(b[4:], p.count)
|
||||
bb = append(bb, b...)
|
||||
p = p.next
|
||||
}
|
||||
|
@ -153,7 +158,7 @@ func (s *sequence) toByteArray() ([]byte, error) {
|
|||
// fromByteArray construct the sequence from the byte array
|
||||
func (s *sequence) fromByteArray(data []byte) error {
|
||||
l := len(data)
|
||||
if l%8 != 0 {
|
||||
if l%12 != 0 {
|
||||
return fmt.Errorf("cannot deserialize byte sequence of lenght %d (%v)", l, data)
|
||||
}
|
||||
|
||||
|
@ -161,8 +166,8 @@ func (s *sequence) fromByteArray(data []byte) error {
|
|||
i := 0
|
||||
for {
|
||||
p.block = binary.BigEndian.Uint32(data[i : i+4])
|
||||
p.count = binary.BigEndian.Uint32(data[i+4 : i+8])
|
||||
i += 8
|
||||
p.count = binary.BigEndian.Uint64(data[i+4 : i+12])
|
||||
i += 12
|
||||
if i == l {
|
||||
break
|
||||
}
|
||||
|
@ -187,7 +192,7 @@ func (h *Handle) getCopy() *Handle {
|
|||
}
|
||||
|
||||
// SetAnyInRange atomically sets the first unset bit in the specified range in the sequence and returns the corresponding ordinal
|
||||
func (h *Handle) SetAnyInRange(start, end uint32) (uint32, error) {
|
||||
func (h *Handle) SetAnyInRange(start, end uint64) (uint64, error) {
|
||||
if end-start <= 0 || end >= h.bits {
|
||||
return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end)
|
||||
}
|
||||
|
@ -198,7 +203,7 @@ func (h *Handle) SetAnyInRange(start, end uint32) (uint32, error) {
|
|||
}
|
||||
|
||||
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal
|
||||
func (h *Handle) SetAny() (uint32, error) {
|
||||
func (h *Handle) SetAny() (uint64, error) {
|
||||
if h.Unselected() == 0 {
|
||||
return invalidPos, errNoBitAvailable
|
||||
}
|
||||
|
@ -206,7 +211,7 @@ func (h *Handle) SetAny() (uint32, error) {
|
|||
}
|
||||
|
||||
// Set atomically sets the corresponding bit in the sequence
|
||||
func (h *Handle) Set(ordinal uint32) error {
|
||||
func (h *Handle) Set(ordinal uint64) error {
|
||||
if err := h.validateOrdinal(ordinal); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -215,7 +220,7 @@ func (h *Handle) Set(ordinal uint32) error {
|
|||
}
|
||||
|
||||
// Unset atomically unsets the corresponding bit in the sequence
|
||||
func (h *Handle) Unset(ordinal uint32) error {
|
||||
func (h *Handle) Unset(ordinal uint64) error {
|
||||
if err := h.validateOrdinal(ordinal); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -225,7 +230,7 @@ func (h *Handle) Unset(ordinal uint32) error {
|
|||
|
||||
// IsSet atomically checks if the ordinal bit is set. In case ordinal
|
||||
// is outside of the bit sequence limits, false is returned.
|
||||
func (h *Handle) IsSet(ordinal uint32) bool {
|
||||
func (h *Handle) IsSet(ordinal uint64) bool {
|
||||
if err := h.validateOrdinal(ordinal); err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -236,15 +241,21 @@ func (h *Handle) IsSet(ordinal uint32) bool {
|
|||
}
|
||||
|
||||
// set/reset the bit
|
||||
func (h *Handle) set(ordinal, start, end uint32, any bool, release bool) (uint32, error) {
|
||||
func (h *Handle) set(ordinal, start, end uint64, any bool, release bool) (uint64, error) {
|
||||
var (
|
||||
bitPos uint32
|
||||
bytePos uint32
|
||||
ret uint32
|
||||
bitPos uint64
|
||||
bytePos uint64
|
||||
ret uint64
|
||||
err error
|
||||
)
|
||||
|
||||
for {
|
||||
if h.store != nil {
|
||||
if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil && err != datastore.ErrKeyNotFound {
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
h.Lock()
|
||||
// Get position if available
|
||||
if release {
|
||||
|
@ -298,7 +309,7 @@ func (h *Handle) set(ordinal, start, end uint32, any bool, release bool) (uint32
|
|||
}
|
||||
|
||||
// checks is needed because to cover the case where the number of bits is not a multiple of blockLen
|
||||
func (h *Handle) validateOrdinal(ordinal uint32) error {
|
||||
func (h *Handle) validateOrdinal(ordinal uint64) error {
|
||||
if ordinal >= h.bits {
|
||||
return fmt.Errorf("bit does not belong to the sequence")
|
||||
}
|
||||
|
@ -306,8 +317,23 @@ func (h *Handle) validateOrdinal(ordinal uint32) error {
|
|||
}
|
||||
|
||||
// Destroy removes from the datastore the data belonging to this handle
|
||||
func (h *Handle) Destroy() {
|
||||
h.deleteFromStore()
|
||||
func (h *Handle) Destroy() error {
|
||||
for {
|
||||
if err := h.deleteFromStore(); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return fmt.Errorf("internal failure while destroying the sequence: %v", err)
|
||||
}
|
||||
// Fetch latest
|
||||
if err := h.store.GetObject(datastore.Key(h.Key()...), h); err != nil {
|
||||
if err == datastore.ErrKeyNotFound { // already removed
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to fetch from store when destroying the sequence: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ToByteArray converts this handle's data into a byte array
|
||||
|
@ -315,9 +341,9 @@ func (h *Handle) ToByteArray() ([]byte, error) {
|
|||
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
ba := make([]byte, 8)
|
||||
binary.BigEndian.PutUint32(ba[0:], h.bits)
|
||||
binary.BigEndian.PutUint32(ba[4:], h.unselected)
|
||||
ba := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(ba[0:], h.bits)
|
||||
binary.BigEndian.PutUint64(ba[8:], h.unselected)
|
||||
bm, err := h.head.toByteArray()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize head: %s", err.Error())
|
||||
|
@ -334,27 +360,27 @@ func (h *Handle) FromByteArray(ba []byte) error {
|
|||
}
|
||||
|
||||
nh := &sequence{}
|
||||
err := nh.fromByteArray(ba[8:])
|
||||
err := nh.fromByteArray(ba[16:])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to deserialize head: %s", err.Error())
|
||||
}
|
||||
|
||||
h.Lock()
|
||||
h.head = nh
|
||||
h.bits = binary.BigEndian.Uint32(ba[0:4])
|
||||
h.unselected = binary.BigEndian.Uint32(ba[4:8])
|
||||
h.bits = binary.BigEndian.Uint64(ba[0:8])
|
||||
h.unselected = binary.BigEndian.Uint64(ba[8:16])
|
||||
h.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bits returns the length of the bit sequence
|
||||
func (h *Handle) Bits() uint32 {
|
||||
func (h *Handle) Bits() uint64 {
|
||||
return h.bits
|
||||
}
|
||||
|
||||
// Unselected returns the number of bits which are not selected
|
||||
func (h *Handle) Unselected() uint32 {
|
||||
func (h *Handle) Unselected() uint64 {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
return h.unselected
|
||||
|
@ -367,8 +393,40 @@ func (h *Handle) String() string {
|
|||
h.app, h.id, h.dbIndex, h.bits, h.unselected, h.head.toString())
|
||||
}
|
||||
|
||||
// MarshalJSON encodes Handle into json message
|
||||
func (h *Handle) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"id": h.id,
|
||||
}
|
||||
|
||||
b, err := h.ToByteArray()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m["sequence"] = b
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes json message into Handle
|
||||
func (h *Handle) UnmarshalJSON(data []byte) error {
|
||||
var (
|
||||
m map[string]interface{}
|
||||
b []byte
|
||||
err error
|
||||
)
|
||||
if err = json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
h.id = m["id"].(string)
|
||||
bi, _ := json.Marshal(m["sequence"])
|
||||
if err := json.Unmarshal(bi, &b); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.FromByteArray(b)
|
||||
}
|
||||
|
||||
// getFirstAvailable looks for the first unset bit in passed mask starting from start
|
||||
func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) {
|
||||
func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
|
||||
// Find sequence which contains the start bit
|
||||
byteStart, bitStart := ordinalToPos(start)
|
||||
current, _, _, inBlockBytePos := findSequence(head, byteStart)
|
||||
|
@ -392,7 +450,7 @@ func getFirstAvailable(head *sequence, start uint32) (uint32, uint32, error) {
|
|||
|
||||
// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset
|
||||
// If the ordinal is beyond the sequence limits, a negative response is returned
|
||||
func checkIfAvailable(head *sequence, ordinal uint32) (uint32, uint32, error) {
|
||||
func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) {
|
||||
bytePos, bitPos := ordinalToPos(ordinal)
|
||||
|
||||
// Find the sequence containing this byte
|
||||
|
@ -412,7 +470,7 @@ func checkIfAvailable(head *sequence, ordinal uint32) (uint32, uint32, error) {
|
|||
// sequence containing the byte (current), the pointer to the previous sequence,
|
||||
// the number of blocks preceding the block containing the byte inside the current sequence.
|
||||
// If bytePos is outside of the list, function will return (nil, nil, 0, invalidPos)
|
||||
func findSequence(head *sequence, bytePos uint32) (*sequence, *sequence, uint32, uint32) {
|
||||
func findSequence(head *sequence, bytePos uint64) (*sequence, *sequence, uint64, uint64) {
|
||||
// Find the sequence containing this byte
|
||||
previous := head
|
||||
current := head
|
||||
|
@ -453,7 +511,7 @@ func findSequence(head *sequence, bytePos uint32) (*sequence, *sequence, uint32,
|
|||
// A) block is first in current: [prev seq] [new] [modified current seq] [next seq]
|
||||
// B) block is last in current: [prev seq] [modified current seq] [new] [next seq]
|
||||
// C) block is in the middle of current: [prev seq] [curr pre] [new] [curr post] [next seq]
|
||||
func pushReservation(bytePos, bitPos uint32, head *sequence, release bool) *sequence {
|
||||
func pushReservation(bytePos, bitPos uint64, head *sequence, release bool) *sequence {
|
||||
// Store list's head
|
||||
newHead := head
|
||||
|
||||
|
@ -541,18 +599,18 @@ func mergeSequences(seq *sequence) {
|
|||
}
|
||||
}
|
||||
|
||||
func getNumBlocks(numBits uint32) uint32 {
|
||||
numBlocks := numBits / blockLen
|
||||
if numBits%blockLen != 0 {
|
||||
func getNumBlocks(numBits uint64) uint64 {
|
||||
numBlocks := numBits / uint64(blockLen)
|
||||
if numBits%uint64(blockLen) != 0 {
|
||||
numBlocks++
|
||||
}
|
||||
return numBlocks
|
||||
}
|
||||
|
||||
func ordinalToPos(ordinal uint32) (uint32, uint32) {
|
||||
func ordinalToPos(ordinal uint64) (uint64, uint64) {
|
||||
return ordinal / 8, ordinal % 8
|
||||
}
|
||||
|
||||
func posToOrdinal(bytePos, bitPos uint32) uint32 {
|
||||
func posToOrdinal(bytePos, bitPos uint64) uint64 {
|
||||
return bytePos*8 + bitPos
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -25,27 +24,16 @@ func (h *Handle) KeyPrefix() []string {
|
|||
|
||||
// Value marshals the data to be stored in the KV store
|
||||
func (h *Handle) Value() []byte {
|
||||
b, err := h.ToByteArray()
|
||||
b, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to serialize Handle: %v", err)
|
||||
b = []byte{}
|
||||
return nil
|
||||
}
|
||||
jv, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to json encode bitseq handler byte array: %v", err)
|
||||
return []byte{}
|
||||
}
|
||||
return jv
|
||||
return b
|
||||
}
|
||||
|
||||
// SetValue unmarshals the data from the KV store
|
||||
func (h *Handle) SetValue(value []byte) error {
|
||||
var b []byte
|
||||
if err := json.Unmarshal(value, &b); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return h.FromByteArray(b)
|
||||
return json.Unmarshal(value, h)
|
||||
}
|
||||
|
||||
// Index returns the latest DB Index as seen by this object
|
||||
|
@ -70,46 +58,46 @@ func (h *Handle) Exists() bool {
|
|||
return h.dbExists
|
||||
}
|
||||
|
||||
// New method returns a handle based on the receiver handle
|
||||
func (h *Handle) New() datastore.KVObject {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
return &Handle{
|
||||
app: h.app,
|
||||
store: h.store,
|
||||
}
|
||||
}
|
||||
|
||||
// CopyTo deep copies the handle into the passed destination object
|
||||
func (h *Handle) CopyTo(o datastore.KVObject) error {
|
||||
h.Lock()
|
||||
defer h.Unlock()
|
||||
|
||||
dstH := o.(*Handle)
|
||||
dstH.bits = h.bits
|
||||
dstH.unselected = h.unselected
|
||||
dstH.head = h.head.getCopy()
|
||||
dstH.app = h.app
|
||||
dstH.id = h.id
|
||||
dstH.dbIndex = h.dbIndex
|
||||
dstH.dbExists = h.dbExists
|
||||
dstH.store = h.store
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip provides a way for a KV Object to avoid persisting it in the KV Store
|
||||
func (h *Handle) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// DataScope method returns the storage scope of the datastore
|
||||
func (h *Handle) DataScope() datastore.DataScope {
|
||||
return datastore.GlobalScope
|
||||
}
|
||||
|
||||
func (h *Handle) watchForChanges() error {
|
||||
func (h *Handle) DataScope() string {
|
||||
h.Lock()
|
||||
store := h.store
|
||||
h.Unlock()
|
||||
defer h.Unlock()
|
||||
|
||||
if store == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
kvpChan, err := store.KVStore().Watch(datastore.Key(h.Key()...), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case kvPair := <-kvpChan:
|
||||
// Only process remote update
|
||||
if kvPair != nil && (kvPair.LastIndex != h.Index()) {
|
||||
err := h.fromDsValue(kvPair.Value)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to reconstruct bitseq handle from ds watch: %s", err.Error())
|
||||
} else {
|
||||
h.SetIndex(kvPair.LastIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
return h.store.Scope()
|
||||
}
|
||||
|
||||
func (h *Handle) fromDsValue(value []byte) error {
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
"github.com/BurntSushi/toml"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/discovery"
|
||||
"github.com/docker/docker/pkg/tlsconfig"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
)
|
||||
|
||||
|
@ -14,12 +16,13 @@ import (
|
|||
type Config struct {
|
||||
Daemon DaemonCfg
|
||||
Cluster ClusterCfg
|
||||
GlobalStore, LocalStore DatastoreCfg
|
||||
Scopes map[string]*datastore.ScopeCfg
|
||||
}
|
||||
|
||||
// DaemonCfg represents libnetwork core configuration
|
||||
type DaemonCfg struct {
|
||||
Debug bool
|
||||
DataDir string
|
||||
DefaultNetwork string
|
||||
DefaultDriver string
|
||||
Labels []string
|
||||
|
@ -34,26 +37,28 @@ type ClusterCfg struct {
|
|||
Heartbeat uint64
|
||||
}
|
||||
|
||||
// DatastoreCfg represents Datastore configuration.
|
||||
type DatastoreCfg struct {
|
||||
Embedded bool
|
||||
Client DatastoreClientCfg
|
||||
}
|
||||
|
||||
// DatastoreClientCfg represents Datastore Client-only mode configuration
|
||||
type DatastoreClientCfg struct {
|
||||
Provider string
|
||||
Address string
|
||||
Config *store.Config
|
||||
// LoadDefaultScopes loads default scope configs for scopes which
|
||||
// doesn't have explicit user specified configs.
|
||||
func (c *Config) LoadDefaultScopes(dataDir string) {
|
||||
for k, v := range datastore.DefaultScopes(dataDir) {
|
||||
if _, ok := c.Scopes[k]; !ok {
|
||||
c.Scopes[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseConfig parses the libnetwork configuration file
|
||||
func ParseConfig(tomlCfgFile string) (*Config, error) {
|
||||
var cfg Config
|
||||
if _, err := toml.DecodeFile(tomlCfgFile, &cfg); err != nil {
|
||||
cfg := &Config{
|
||||
Scopes: map[string]*datastore.ScopeCfg{},
|
||||
}
|
||||
|
||||
if _, err := toml.DecodeFile(tomlCfgFile, cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cfg, nil
|
||||
|
||||
cfg.LoadDefaultScopes(cfg.Daemon.DataDir)
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// Option is a option setter function type used to pass varios configurations
|
||||
|
@ -63,7 +68,7 @@ type Option func(c *Config)
|
|||
// OptionDefaultNetwork function returns an option setter for a default network
|
||||
func OptionDefaultNetwork(dn string) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option DefaultNetwork: %s", dn)
|
||||
log.Debugf("Option DefaultNetwork: %s", dn)
|
||||
c.Daemon.DefaultNetwork = strings.TrimSpace(dn)
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +76,7 @@ func OptionDefaultNetwork(dn string) Option {
|
|||
// OptionDefaultDriver function returns an option setter for default driver
|
||||
func OptionDefaultDriver(dd string) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option DefaultDriver: %s", dd)
|
||||
log.Debugf("Option DefaultDriver: %s", dd)
|
||||
c.Daemon.DefaultDriver = strings.TrimSpace(dd)
|
||||
}
|
||||
}
|
||||
|
@ -97,16 +102,56 @@ func OptionLabels(labels []string) Option {
|
|||
// OptionKVProvider function returns an option setter for kvstore provider
|
||||
func OptionKVProvider(provider string) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option OptionKVProvider: %s", provider)
|
||||
c.GlobalStore.Client.Provider = strings.TrimSpace(provider)
|
||||
log.Debugf("Option OptionKVProvider: %s", provider)
|
||||
if _, ok := c.Scopes[datastore.GlobalScope]; !ok {
|
||||
c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{}
|
||||
}
|
||||
c.Scopes[datastore.GlobalScope].Client.Provider = strings.TrimSpace(provider)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionKVProviderURL function returns an option setter for kvstore url
|
||||
func OptionKVProviderURL(url string) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option OptionKVProviderURL: %s", url)
|
||||
c.GlobalStore.Client.Address = strings.TrimSpace(url)
|
||||
log.Debugf("Option OptionKVProviderURL: %s", url)
|
||||
if _, ok := c.Scopes[datastore.GlobalScope]; !ok {
|
||||
c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{}
|
||||
}
|
||||
c.Scopes[datastore.GlobalScope].Client.Address = strings.TrimSpace(url)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionKVOpts function returns an option setter for kvstore options
|
||||
func OptionKVOpts(opts map[string]string) Option {
|
||||
return func(c *Config) {
|
||||
if opts["kv.cacertfile"] != "" && opts["kv.certfile"] != "" && opts["kv.keyfile"] != "" {
|
||||
log.Info("Option Initializing KV with TLS")
|
||||
tlsConfig, err := tlsconfig.Client(tlsconfig.Options{
|
||||
CAFile: opts["kv.cacertfile"],
|
||||
CertFile: opts["kv.certfile"],
|
||||
KeyFile: opts["kv.keyfile"],
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("Unable to set up TLS: %s", err)
|
||||
return
|
||||
}
|
||||
if _, ok := c.Scopes[datastore.GlobalScope]; !ok {
|
||||
c.Scopes[datastore.GlobalScope] = &datastore.ScopeCfg{}
|
||||
}
|
||||
if c.Scopes[datastore.GlobalScope].Client.Config == nil {
|
||||
c.Scopes[datastore.GlobalScope].Client.Config = &store.Config{TLS: tlsConfig}
|
||||
} else {
|
||||
c.Scopes[datastore.GlobalScope].Client.Config.TLS = tlsConfig
|
||||
}
|
||||
// Workaround libkv/etcd bug for https
|
||||
c.Scopes[datastore.GlobalScope].Client.Config.ClientTLS = &store.ClientTLSConfig{
|
||||
CACertFile: opts["kv.cacertfile"],
|
||||
CertFile: opts["kv.certfile"],
|
||||
KeyFile: opts["kv.keyfile"],
|
||||
}
|
||||
} else {
|
||||
log.Info("Option Initializing KV without TLS")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,6 +169,13 @@ func OptionDiscoveryAddress(address string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// OptionDataDir function returns an option setter for data folder
|
||||
func OptionDataDir(dataDir string) Option {
|
||||
return func(c *Config) {
|
||||
c.Daemon.DataDir = dataDir
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessOptions processes options and stores it in config
|
||||
func (c *Config) ProcessOptions(options ...Option) {
|
||||
for _, opt := range options {
|
||||
|
@ -135,7 +187,7 @@ func (c *Config) ProcessOptions(options ...Option) {
|
|||
|
||||
// IsValidName validates configuration objects supported by libnetwork
|
||||
func IsValidName(name string) bool {
|
||||
if strings.TrimSpace(name) == "" || strings.Contains(name, ".") {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -144,23 +196,32 @@ func IsValidName(name string) bool {
|
|||
// OptionLocalKVProvider function returns an option setter for kvstore provider
|
||||
func OptionLocalKVProvider(provider string) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option OptionLocalKVProvider: %s", provider)
|
||||
c.LocalStore.Client.Provider = strings.TrimSpace(provider)
|
||||
log.Debugf("Option OptionLocalKVProvider: %s", provider)
|
||||
if _, ok := c.Scopes[datastore.LocalScope]; !ok {
|
||||
c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{}
|
||||
}
|
||||
c.Scopes[datastore.LocalScope].Client.Provider = strings.TrimSpace(provider)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionLocalKVProviderURL function returns an option setter for kvstore url
|
||||
func OptionLocalKVProviderURL(url string) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option OptionLocalKVProviderURL: %s", url)
|
||||
c.LocalStore.Client.Address = strings.TrimSpace(url)
|
||||
log.Debugf("Option OptionLocalKVProviderURL: %s", url)
|
||||
if _, ok := c.Scopes[datastore.LocalScope]; !ok {
|
||||
c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{}
|
||||
}
|
||||
c.Scopes[datastore.LocalScope].Client.Address = strings.TrimSpace(url)
|
||||
}
|
||||
}
|
||||
|
||||
// OptionLocalKVProviderConfig function returns an option setter for kvstore config
|
||||
func OptionLocalKVProviderConfig(config *store.Config) Option {
|
||||
return func(c *Config) {
|
||||
log.Infof("Option OptionLocalKVProviderConfig: %v", config)
|
||||
c.LocalStore.Client.Config = config
|
||||
log.Debugf("Option OptionLocalKVProviderConfig: %v", config)
|
||||
if _, ok := c.Scopes[datastore.LocalScope]; !ok {
|
||||
c.Scopes[datastore.LocalScope] = &datastore.ScopeCfg{}
|
||||
}
|
||||
c.Scopes[datastore.LocalScope].Client.Config = config
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ import (
|
|||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/hostdiscovery"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -116,60 +118,82 @@ type driverData struct {
|
|||
capability driverapi.Capability
|
||||
}
|
||||
|
||||
type ipamData struct {
|
||||
driver ipamapi.Ipam
|
||||
// default address spaces are provided by ipam driver at registration time
|
||||
defaultLocalAddressSpace, defaultGlobalAddressSpace string
|
||||
}
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
type networkTable map[string]*network
|
||||
type endpointTable map[string]*endpoint
|
||||
|
||||
//type networkTable map[string]*network
|
||||
//type endpointTable map[string]*endpoint
|
||||
type ipamTable map[string]*ipamData
|
||||
type sandboxTable map[string]*sandbox
|
||||
|
||||
type controller struct {
|
||||
id string
|
||||
networks networkTable
|
||||
//networks networkTable
|
||||
drivers driverTable
|
||||
ipamDrivers ipamTable
|
||||
sandboxes sandboxTable
|
||||
cfg *config.Config
|
||||
globalStore, localStore datastore.DataStore
|
||||
stores []datastore.DataStore
|
||||
discovery hostdiscovery.HostDiscovery
|
||||
extKeyListener net.Listener
|
||||
watchCh chan *endpoint
|
||||
unWatchCh chan *endpoint
|
||||
svcDb map[string]svcMap
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// New creates a new instance of network controller.
|
||||
func New(cfgOptions ...config.Option) (NetworkController, error) {
|
||||
var cfg *config.Config
|
||||
if len(cfgOptions) > 0 {
|
||||
cfg = &config.Config{
|
||||
Daemon: config.DaemonCfg{
|
||||
DriverCfg: make(map[string]interface{}),
|
||||
},
|
||||
Scopes: make(map[string]*datastore.ScopeCfg),
|
||||
}
|
||||
|
||||
if len(cfgOptions) > 0 {
|
||||
cfg.ProcessOptions(cfgOptions...)
|
||||
}
|
||||
cfg.LoadDefaultScopes(cfg.Daemon.DataDir)
|
||||
|
||||
c := &controller{
|
||||
id: stringid.GenerateRandomID(),
|
||||
cfg: cfg,
|
||||
networks: networkTable{},
|
||||
sandboxes: sandboxTable{},
|
||||
drivers: driverTable{}}
|
||||
if err := initDrivers(c); err != nil {
|
||||
drivers: driverTable{},
|
||||
ipamDrivers: ipamTable{},
|
||||
svcDb: make(map[string]svcMap),
|
||||
}
|
||||
|
||||
if err := c.initStores(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if cfg != nil {
|
||||
if err := c.initGlobalStore(); err != nil {
|
||||
// Failing to initalize datastore is a bad situation to be in.
|
||||
// But it cannot fail creating the Controller
|
||||
log.Debugf("Failed to Initialize Datastore due to %v. Operating in non-clustered mode", err)
|
||||
}
|
||||
if cfg != nil && cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(cfg.Cluster.Watcher); err != nil {
|
||||
// Failing to initalize discovery is a bad situation to be in.
|
||||
// But it cannot fail creating the Controller
|
||||
log.Debugf("Failed to Initialize Discovery : %v", err)
|
||||
}
|
||||
if err := c.initLocalStore(); err != nil {
|
||||
log.Debugf("Failed to Initialize LocalDatastore due to %v.", err)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
if err := c.startExternalKeyListener(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -272,43 +296,91 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error {
|
||||
if !config.IsValidName(name) {
|
||||
return ErrInvalidName(name)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
_, ok := c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if ok {
|
||||
return driverapi.ErrActiveRegistration(name)
|
||||
}
|
||||
locAS, glbAS, err := driver.GetDefaultAddressSpaces()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ipam driver %s failed to return default address spaces: %v", name, err)
|
||||
}
|
||||
c.Lock()
|
||||
c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS}
|
||||
c.Unlock()
|
||||
|
||||
log.Debugf("Registering ipam provider: %s", name)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if !config.IsValidName(name) {
|
||||
return nil, ErrInvalidName(name)
|
||||
}
|
||||
// Check if a network already exists with the specified network name
|
||||
c.Lock()
|
||||
for _, n := range c.networks {
|
||||
if n.name == name {
|
||||
c.Unlock()
|
||||
return nil, NetworkNameError(name)
|
||||
}
|
||||
}
|
||||
c.Unlock()
|
||||
|
||||
// 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(),
|
||||
ctrlr: c,
|
||||
endpoints: endpointTable{},
|
||||
persist: true,
|
||||
drvOnce: &sync.Once{},
|
||||
}
|
||||
|
||||
network.processOptions(options...)
|
||||
|
||||
if err := c.addNetwork(network); err != nil {
|
||||
// Make sure we have a driver available for this network type
|
||||
// before we allocate anything.
|
||||
if _, err := network.driver(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := c.updateToStore(network); err != nil {
|
||||
log.Warnf("couldnt create network %s: %v", network.name, err)
|
||||
if e := network.Delete(); e != nil {
|
||||
log.Warnf("couldnt cleanup network %s: %v", network.name, err)
|
||||
err := network.ipamAllocate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
network.ipamRelease()
|
||||
}
|
||||
}()
|
||||
|
||||
if err := c.addNetwork(network); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if e := network.deleteNetwork(); e != nil {
|
||||
log.Warnf("couldn't roll back driver network on network %s creation failure: %v", network.name, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = c.updateToStore(network); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if e := c.deleteFromStore(network); e != nil {
|
||||
log.Warnf("couldnt rollback from store, network %s on failure (%v): %v", network.name, err, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
network.epCnt = &endpointCnt{n: network}
|
||||
if err = c.updateToStore(network.epCnt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -316,49 +388,28 @@ func (c *controller) NewNetwork(networkType, name string, options ...NetworkOpti
|
|||
}
|
||||
|
||||
func (c *controller) addNetwork(n *network) error {
|
||||
|
||||
c.Lock()
|
||||
// Check if a driver for the specified network type is available
|
||||
dd, ok := c.drivers[n.networkType]
|
||||
c.Unlock()
|
||||
|
||||
if !ok {
|
||||
var err error
|
||||
dd, err = c.loadDriver(n.networkType)
|
||||
d, err := n.driver()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
n.svcRecords = svcMap{}
|
||||
n.driver = dd.driver
|
||||
n.dataScope = dd.capability.DataScope
|
||||
d := n.driver
|
||||
n.Unlock()
|
||||
|
||||
// Create the network
|
||||
if err := d.CreateNetwork(n.id, n.generic); err != nil {
|
||||
if err := d.CreateNetwork(n.id, n.generic, n.getIPData(4), n.getIPData(6)); err != nil {
|
||||
return err
|
||||
}
|
||||
if n.isGlobalScoped() {
|
||||
if err := n.watchEndpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.Lock()
|
||||
c.networks[n.id] = n
|
||||
c.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) Networks() []Network {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
var list []Network
|
||||
|
||||
list := make([]Network, 0, len(c.networks))
|
||||
for _, n := range c.networks {
|
||||
networks, err := c.getNetworksFromStore()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
for _, n := range networks {
|
||||
list = append(list, n)
|
||||
}
|
||||
|
||||
|
@ -400,12 +451,13 @@ func (c *controller) NetworkByID(id string) (Network, error) {
|
|||
if id == "" {
|
||||
return nil, ErrInvalidID(id)
|
||||
}
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if n, ok := c.networks[id]; ok {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
n, err := c.getNetworkFromStore(id)
|
||||
if err != nil {
|
||||
return nil, ErrNoSuchNetwork(id)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// NewSandbox creates a new sandbox for the passed container id
|
||||
|
@ -456,6 +508,18 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S
|
|||
c.Lock()
|
||||
c.sandboxes[sb.id] = sb
|
||||
c.Unlock()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
c.Lock()
|
||||
delete(c.sandboxes, sb.id)
|
||||
c.Unlock()
|
||||
}
|
||||
}()
|
||||
|
||||
err = sb.storeUpdate()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("updating the store state of sandbox failed: %v", err)
|
||||
}
|
||||
|
||||
return sb, nil
|
||||
}
|
||||
|
@ -534,20 +598,43 @@ func (c *controller) loadDriver(networkType string) (*driverData, error) {
|
|||
return dd, nil
|
||||
}
|
||||
|
||||
func (c *controller) getDriver(networkType string) (*driverData, error) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
dd, ok := c.drivers[networkType]
|
||||
if !ok {
|
||||
return nil, types.NotFoundErrorf("driver %s not found", networkType)
|
||||
func (c *controller) loadIpamDriver(name string) (*ipamData, error) {
|
||||
if _, err := plugins.Get(name, ipamapi.PluginEndpointType); err != nil {
|
||||
if err == plugins.ErrNotFound {
|
||||
return nil, types.NotFoundErrorf(err.Error())
|
||||
}
|
||||
return dd, nil
|
||||
return nil, err
|
||||
}
|
||||
c.Lock()
|
||||
id, ok := c.ipamDrivers[name]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
return nil, ErrInvalidNetworkDriver(name)
|
||||
}
|
||||
return id, 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, error) {
|
||||
id, err := c.getIPAM(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return id.driver, nil
|
||||
}
|
||||
|
||||
func (c *controller) Stop() {
|
||||
if c.localStore != nil {
|
||||
c.localStore.KVStore().Close()
|
||||
}
|
||||
c.closeStores()
|
||||
c.stopExternalKeyListener()
|
||||
osl.GC()
|
||||
}
|
||||
|
|
153
vendor/src/github.com/docker/libnetwork/datastore/cache.go
vendored
Normal file
153
vendor/src/github.com/docker/libnetwork/datastore/cache.go
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
)
|
||||
|
||||
type kvMap map[string]KVObject
|
||||
|
||||
type cache struct {
|
||||
sync.Mutex
|
||||
kmm map[string]kvMap
|
||||
ds *datastore
|
||||
}
|
||||
|
||||
func newCache(ds *datastore) *cache {
|
||||
return &cache{kmm: make(map[string]kvMap), ds: ds}
|
||||
}
|
||||
|
||||
func (c *cache) kmap(kvObject KVObject) (kvMap, error) {
|
||||
var err error
|
||||
|
||||
c.Lock()
|
||||
keyPrefix := Key(kvObject.KeyPrefix()...)
|
||||
kmap, ok := c.kmm[keyPrefix]
|
||||
c.Unlock()
|
||||
|
||||
if ok {
|
||||
return kmap, nil
|
||||
}
|
||||
|
||||
kmap = kvMap{}
|
||||
|
||||
// Bail out right away if the kvObject does not implement KVConstructor
|
||||
ctor, ok := kvObject.(KVConstructor)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error while populating kmap, object does not implement KVConstructor interface")
|
||||
}
|
||||
|
||||
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 the store doesn't have anything then there is nothing to
|
||||
// populate in the cache. Just bail out.
|
||||
goto out
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("error while populating kmap: %v", err)
|
||||
}
|
||||
|
||||
for _, kvPair := range kvList {
|
||||
// Ignore empty kvPair values
|
||||
if len(kvPair.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
dstO := ctor.New()
|
||||
err = dstO.SetValue(kvPair.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the object has a correct view of the DB index in
|
||||
// case we need to modify it and update the DB.
|
||||
dstO.SetIndex(kvPair.LastIndex)
|
||||
|
||||
kmap[Key(dstO.Key()...)] = dstO
|
||||
}
|
||||
|
||||
out:
|
||||
// There may multiple go routines racing to fill the
|
||||
// cache. The one which places the kmap in c.kmm first
|
||||
// wins. The others should just use what the first populated.
|
||||
c.Lock()
|
||||
kmapNew, ok := c.kmm[keyPrefix]
|
||||
if ok {
|
||||
c.Unlock()
|
||||
return kmapNew, nil
|
||||
}
|
||||
|
||||
c.kmm[keyPrefix] = kmap
|
||||
c.Unlock()
|
||||
|
||||
return kmap, nil
|
||||
}
|
||||
|
||||
func (c *cache) add(kvObject KVObject) error {
|
||||
kmap, err := c.kmap(kvObject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
kmap[Key(kvObject.Key()...)] = kvObject
|
||||
c.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cache) del(kvObject KVObject) error {
|
||||
kmap, err := c.kmap(kvObject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
delete(kmap, Key(kvObject.Key()...))
|
||||
c.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *cache) get(key string, kvObject KVObject) error {
|
||||
kmap, err := c.kmap(kvObject)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
o, ok := kmap[Key(kvObject.Key()...)]
|
||||
if !ok {
|
||||
return ErrKeyNotFound
|
||||
}
|
||||
|
||||
ctor, ok := o.(KVConstructor)
|
||||
if !ok {
|
||||
return fmt.Errorf("kvobject does not implement KVConstructor interface. could not get object")
|
||||
}
|
||||
|
||||
return ctor.CopyTo(kvObject)
|
||||
}
|
||||
|
||||
func (c *cache) list(kvObject KVObject) ([]KVObject, error) {
|
||||
kmap, err := c.kmap(kvObject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
var kvol []KVObject
|
||||
for _, v := range kmap {
|
||||
kvol = append(kvol, v)
|
||||
}
|
||||
|
||||
return kvol, nil
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libkv"
|
||||
"github.com/docker/libkv/store"
|
||||
|
@ -10,26 +13,37 @@ import (
|
|||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/docker/libkv/store/zookeeper"
|
||||
"github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
//DataStore exported
|
||||
type DataStore interface {
|
||||
// GetObject gets data from datastore and unmarshals to the specified object
|
||||
GetObject(key string, o KV) error
|
||||
GetObject(key string, o KVObject) error
|
||||
// PutObject adds a new Record based on an object into the datastore
|
||||
PutObject(kvObject KV) error
|
||||
PutObject(kvObject KVObject) error
|
||||
// PutObjectAtomic provides an atomic add and update operation for a Record
|
||||
PutObjectAtomic(kvObject KV) error
|
||||
PutObjectAtomic(kvObject KVObject) error
|
||||
// DeleteObject deletes a record
|
||||
DeleteObject(kvObject KV) error
|
||||
DeleteObject(kvObject KVObject) error
|
||||
// DeleteObjectAtomic performs an atomic delete operation
|
||||
DeleteObjectAtomic(kvObject KV) error
|
||||
DeleteObjectAtomic(kvObject KVObject) error
|
||||
// DeleteTree deletes a record
|
||||
DeleteTree(kvObject KV) error
|
||||
DeleteTree(kvObject KVObject) error
|
||||
// Watchable returns whether the store is watchable are not
|
||||
Watchable() bool
|
||||
// Watch for changes on a KVObject
|
||||
Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error)
|
||||
// List returns of a list of KVObjects belonging to the parent
|
||||
// key. The caller must pass a KVObject of the same type as
|
||||
// the objects that need to be listed
|
||||
List(string, KVObject) ([]KVObject, error)
|
||||
// Scope returns the scope of the store
|
||||
Scope() string
|
||||
// KVStore returns access to the KV Store
|
||||
KVStore() store.Store
|
||||
// Close closes the data store
|
||||
Close()
|
||||
}
|
||||
|
||||
// ErrKeyModified is raised for an atomic update when the update is working on a stale state
|
||||
|
@ -39,11 +53,14 @@ var (
|
|||
)
|
||||
|
||||
type datastore struct {
|
||||
scope string
|
||||
store store.Store
|
||||
cache *cache
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
//KV Key Value interface used by objects to be part of the DataStore
|
||||
type KV interface {
|
||||
// KVObject is Key/Value interface used by objects to be part of the DataStore
|
||||
type KVObject interface {
|
||||
// Key method lets an object to provide the Key to be used in KV Store
|
||||
Key() []string
|
||||
// KeyPrefix method lets an object to return immediate parent key that can be used for tree walk
|
||||
|
@ -60,19 +77,39 @@ type KV interface {
|
|||
// When SetIndex() is called, the object has been stored.
|
||||
Exists() bool
|
||||
// DataScope indicates the storage scope of the KV object
|
||||
DataScope() DataScope
|
||||
DataScope() string
|
||||
// Skip provides a way for a KV Object to avoid persisting it in the KV Store
|
||||
Skip() bool
|
||||
}
|
||||
|
||||
// DataScope indicates the storage scope
|
||||
type DataScope int
|
||||
// KVConstructor interface defines methods which can construct a KVObject from another.
|
||||
type KVConstructor interface {
|
||||
// New returns a new object which is created based on the
|
||||
// source object
|
||||
New() KVObject
|
||||
// CopyTo deep copies the contents of the implementing object
|
||||
// to the passed destination object
|
||||
CopyTo(KVObject) error
|
||||
}
|
||||
|
||||
// ScopeCfg represents Datastore configuration.
|
||||
type ScopeCfg struct {
|
||||
Client ScopeClientCfg
|
||||
}
|
||||
|
||||
// ScopeClientCfg represents Datastore Client-only mode configuration
|
||||
type ScopeClientCfg struct {
|
||||
Provider string
|
||||
Address string
|
||||
Config *store.Config
|
||||
}
|
||||
|
||||
const (
|
||||
// LocalScope indicates to store the KV object in local datastore such as boltdb
|
||||
LocalScope DataScope = iota
|
||||
LocalScope = "local"
|
||||
// GlobalScope indicates to store the KV object in global datastore such as consul/etcd/zookeeper
|
||||
GlobalScope
|
||||
GlobalScope = "global"
|
||||
defaultPrefix = "/var/lib/docker/network/files"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -82,7 +119,26 @@ const (
|
|||
EndpointKeyPrefix = "endpoint"
|
||||
)
|
||||
|
||||
var rootChain = []string{"docker", "libnetwork"}
|
||||
var (
|
||||
defaultScopes = makeDefaultScopes()
|
||||
)
|
||||
|
||||
func makeDefaultScopes() map[string]*ScopeCfg {
|
||||
def := make(map[string]*ScopeCfg)
|
||||
def[LocalScope] = &ScopeCfg{
|
||||
Client: ScopeClientCfg{
|
||||
Provider: "boltdb",
|
||||
Address: defaultPrefix + "/local-kv.db",
|
||||
Config: &store.Config{
|
||||
Bucket: "libnetwork",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return def
|
||||
}
|
||||
|
||||
var rootChain = []string{"docker", "network", "v1.0"}
|
||||
|
||||
func init() {
|
||||
consul.Register()
|
||||
|
@ -91,6 +147,28 @@ func init() {
|
|||
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 != "" {
|
||||
defaultScopes[LocalScope].Client.Address = dataDir + "/network/files/local-kv.db"
|
||||
return defaultScopes
|
||||
}
|
||||
|
||||
defaultScopes[LocalScope].Client.Address = defaultPrefix + "/local-kv.db"
|
||||
return defaultScopes
|
||||
}
|
||||
|
||||
// IsValid checks if the scope config has valid configuration.
|
||||
func (cfg *ScopeCfg) IsValid() bool {
|
||||
if cfg == nil ||
|
||||
strings.TrimSpace(cfg.Client.Provider) == "" ||
|
||||
strings.TrimSpace(cfg.Client.Address) == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//Key provides convenient method to create a Key
|
||||
func Key(key ...string) string {
|
||||
keychain := append(rootChain, key...)
|
||||
|
@ -110,7 +188,11 @@ func ParseKey(key string) ([]string, error) {
|
|||
}
|
||||
|
||||
// newClient used to connect to KV Store
|
||||
func newClient(kv string, addrs string, config *store.Config) (DataStore, error) {
|
||||
func newClient(scope string, kv string, addrs string, config *store.Config, cached bool) (DataStore, error) {
|
||||
if cached && scope != LocalScope {
|
||||
return nil, fmt.Errorf("caching supported only for scope %s", LocalScope)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = &store.Config{}
|
||||
}
|
||||
|
@ -118,22 +200,82 @@ func newClient(kv string, addrs string, config *store.Config) (DataStore, error)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &datastore{store: store}
|
||||
|
||||
ds := &datastore{scope: scope, store: store}
|
||||
if cached {
|
||||
ds.cache = newCache(ds)
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
// NewDataStore creates a new instance of LibKV data store
|
||||
func NewDataStore(cfg *config.DatastoreCfg) (DataStore, error) {
|
||||
if cfg == nil {
|
||||
return nil, types.BadRequestErrorf("invalid configuration passed to datastore")
|
||||
func NewDataStore(scope string, cfg *ScopeCfg) (DataStore, error) {
|
||||
if cfg == nil || cfg.Client.Provider == "" || cfg.Client.Address == "" {
|
||||
c, ok := defaultScopes[scope]
|
||||
if !ok || c.Client.Provider == "" || c.Client.Address == "" {
|
||||
return nil, fmt.Errorf("unexpected scope %s without configuration passed", scope)
|
||||
}
|
||||
// TODO : cfg.Embedded case
|
||||
return newClient(cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config)
|
||||
|
||||
cfg = c
|
||||
}
|
||||
|
||||
var cached bool
|
||||
if scope == LocalScope {
|
||||
cached = true
|
||||
}
|
||||
|
||||
return newClient(scope, cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config, cached)
|
||||
}
|
||||
|
||||
// NewCustomDataStore can be used by clients to plugin cusom datatore that adhers to store.Store
|
||||
func NewCustomDataStore(customStore store.Store) DataStore {
|
||||
return &datastore{store: customStore}
|
||||
func (ds *datastore) Close() {
|
||||
ds.store.Close()
|
||||
}
|
||||
|
||||
func (ds *datastore) Scope() string {
|
||||
return ds.scope
|
||||
}
|
||||
|
||||
func (ds *datastore) Watchable() bool {
|
||||
return ds.scope != LocalScope
|
||||
}
|
||||
|
||||
func (ds *datastore) Watch(kvObject KVObject, stopCh <-chan struct{}) (<-chan KVObject, error) {
|
||||
sCh := make(chan struct{})
|
||||
|
||||
ctor, ok := kvObject.(KVConstructor)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error watching object type %T, object does not implement KVConstructor interface", kvObject)
|
||||
}
|
||||
|
||||
kvpCh, err := ds.store.Watch(Key(kvObject.Key()...), sCh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvoCh := make(chan KVObject)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
close(sCh)
|
||||
return
|
||||
case kvPair := <-kvpCh:
|
||||
dstO := ctor.New()
|
||||
|
||||
if err := dstO.SetValue(kvPair.Value); err != nil {
|
||||
log.Printf("Could not unmarshal kvpair value = %s", string(kvPair.Value))
|
||||
break
|
||||
}
|
||||
|
||||
dstO.SetIndex(kvPair.LastIndex)
|
||||
kvoCh <- dstO
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return kvoCh, nil
|
||||
}
|
||||
|
||||
func (ds *datastore) KVStore() store.Store {
|
||||
|
@ -141,40 +283,76 @@ func (ds *datastore) KVStore() store.Store {
|
|||
}
|
||||
|
||||
// PutObjectAtomic adds a new Record based on an object into the datastore
|
||||
func (ds *datastore) PutObjectAtomic(kvObject KV) error {
|
||||
func (ds *datastore) PutObjectAtomic(kvObject KVObject) error {
|
||||
var (
|
||||
previous *store.KVPair
|
||||
pair *store.KVPair
|
||||
err error
|
||||
)
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
if kvObject == nil {
|
||||
return types.BadRequestErrorf("invalid KV Object : nil")
|
||||
}
|
||||
|
||||
kvObjValue := kvObject.Value()
|
||||
|
||||
if kvObjValue == nil {
|
||||
return types.BadRequestErrorf("invalid KV Object with a nil Value for key %s", Key(kvObject.Key()...))
|
||||
}
|
||||
|
||||
var previous *store.KVPair
|
||||
if kvObject.Skip() {
|
||||
goto add_cache
|
||||
}
|
||||
|
||||
if kvObject.Exists() {
|
||||
previous = &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()}
|
||||
} else {
|
||||
previous = nil
|
||||
}
|
||||
_, pair, err := ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil)
|
||||
|
||||
_, pair, err = ds.store.AtomicPut(Key(kvObject.Key()...), kvObjValue, previous, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kvObject.SetIndex(pair.LastIndex)
|
||||
|
||||
add_cache:
|
||||
if ds.cache != nil {
|
||||
return ds.cache.add(kvObject)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutObject adds a new Record based on an object into the datastore
|
||||
func (ds *datastore) PutObject(kvObject KV) error {
|
||||
func (ds *datastore) PutObject(kvObject KVObject) error {
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
if kvObject == nil {
|
||||
return types.BadRequestErrorf("invalid KV Object : nil")
|
||||
}
|
||||
return ds.putObjectWithKey(kvObject, kvObject.Key()...)
|
||||
|
||||
if kvObject.Skip() {
|
||||
goto add_cache
|
||||
}
|
||||
|
||||
if err := ds.putObjectWithKey(kvObject, kvObject.Key()...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
add_cache:
|
||||
if ds.cache != nil {
|
||||
return ds.cache.add(kvObject)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *datastore) putObjectWithKey(kvObject KV, key ...string) error {
|
||||
func (ds *datastore) putObjectWithKey(kvObject KVObject, key ...string) error {
|
||||
kvObjValue := kvObject.Value()
|
||||
|
||||
if kvObjValue == nil {
|
||||
|
@ -184,39 +362,143 @@ func (ds *datastore) putObjectWithKey(kvObject KV, key ...string) error {
|
|||
}
|
||||
|
||||
// GetObject returns a record matching the key
|
||||
func (ds *datastore) GetObject(key string, o KV) error {
|
||||
func (ds *datastore) GetObject(key string, o KVObject) error {
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
if ds.cache != nil {
|
||||
return ds.cache.get(key, o)
|
||||
}
|
||||
|
||||
kvPair, err := ds.store.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = o.SetValue(kvPair.Value)
|
||||
if err != nil {
|
||||
|
||||
if err := o.SetValue(kvPair.Value); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Make sure the object has a correct view of the DB index in case we need to modify it
|
||||
// and update the DB.
|
||||
// Make sure the object has a correct view of the DB index in
|
||||
// case we need to modify it and update the DB.
|
||||
o.SetIndex(kvPair.LastIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ds *datastore) ensureKey(key string) error {
|
||||
exists, err := ds.store.Exists(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
return ds.store.Put(key, []byte{}, nil)
|
||||
}
|
||||
|
||||
func (ds *datastore) List(key string, kvObject KVObject) ([]KVObject, error) {
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
if ds.cache != nil {
|
||||
return ds.cache.list(kvObject)
|
||||
}
|
||||
|
||||
// Bail out right away if the kvObject does not implement KVConstructor
|
||||
ctor, ok := kvObject.(KVConstructor)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("error listing objects, object does not implement KVConstructor interface")
|
||||
}
|
||||
|
||||
// Make sure the parent key exists
|
||||
if err := ds.ensureKey(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kvList, err := ds.store.List(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kvol []KVObject
|
||||
for _, kvPair := range kvList {
|
||||
if len(kvPair.Value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
dstO := ctor.New()
|
||||
if err := dstO.SetValue(kvPair.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the object has a correct view of the DB index in
|
||||
// case we need to modify it and update the DB.
|
||||
dstO.SetIndex(kvPair.LastIndex)
|
||||
|
||||
kvol = append(kvol, dstO)
|
||||
}
|
||||
|
||||
return kvol, nil
|
||||
}
|
||||
|
||||
// DeleteObject unconditionally deletes a record from the store
|
||||
func (ds *datastore) DeleteObject(kvObject KV) error {
|
||||
func (ds *datastore) DeleteObject(kvObject KVObject) error {
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
// cleaup the cache first
|
||||
if ds.cache != nil {
|
||||
ds.cache.del(kvObject)
|
||||
}
|
||||
|
||||
if kvObject.Skip() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ds.store.Delete(Key(kvObject.Key()...))
|
||||
}
|
||||
|
||||
// DeleteObjectAtomic performs atomic delete on a record
|
||||
func (ds *datastore) DeleteObjectAtomic(kvObject KV) error {
|
||||
func (ds *datastore) DeleteObjectAtomic(kvObject KVObject) error {
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
if kvObject == nil {
|
||||
return types.BadRequestErrorf("invalid KV Object : nil")
|
||||
}
|
||||
|
||||
previous := &store.KVPair{Key: Key(kvObject.Key()...), LastIndex: kvObject.Index()}
|
||||
_, err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous)
|
||||
|
||||
if kvObject.Skip() {
|
||||
goto del_cache
|
||||
}
|
||||
|
||||
if _, err := ds.store.AtomicDelete(Key(kvObject.Key()...), previous); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
del_cache:
|
||||
// cleanup the cache only if AtomicDelete went through successfully
|
||||
if ds.cache != nil {
|
||||
return ds.cache.del(kvObject)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTree unconditionally deletes a record from the store
|
||||
func (ds *datastore) DeleteTree(kvObject KV) error {
|
||||
func (ds *datastore) DeleteTree(kvObject KVObject) error {
|
||||
ds.Lock()
|
||||
defer ds.Unlock()
|
||||
|
||||
// cleaup the cache first
|
||||
if ds.cache != nil {
|
||||
ds.cache.del(kvObject)
|
||||
}
|
||||
|
||||
if kvObject.Skip() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ds.store.DeleteTree(Key(kvObject.KeyPrefix()...))
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
|
@ -92,25 +91,6 @@ func (sb *sandbox) clearDefaultGW() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) createGWNetwork() (Network, error) {
|
||||
netOption := options.Generic{
|
||||
"BridgeName": libnGWNetwork,
|
||||
"EnableICC": false,
|
||||
"EnableIPMasquerade": true,
|
||||
}
|
||||
|
||||
n, err := c.NewNetwork("bridge", libnGWNetwork,
|
||||
NetworkOptionGeneric(options.Generic{
|
||||
netlabel.GenericData: netOption,
|
||||
netlabel.EnableIPv6: false,
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating external connectivity network: %v", err)
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (sb *sandbox) needDefaultGW() bool {
|
||||
var needGW bool
|
||||
|
||||
|
|
7
vendor/src/github.com/docker/libnetwork/default_gateway_freebsd.go
vendored
Normal file
7
vendor/src/github.com/docker/libnetwork/default_gateway_freebsd.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 freebsd")
|
||||
}
|
29
vendor/src/github.com/docker/libnetwork/default_gateway_linux.go
vendored
Normal file
29
vendor/src/github.com/docker/libnetwork/default_gateway_linux.go
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/libnetwork/drivers/bridge"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
)
|
||||
|
||||
func (c *controller) createGWNetwork() (Network, error) {
|
||||
netOption := map[string]string{
|
||||
bridge.BridgeName: libnGWNetwork,
|
||||
bridge.EnableICC: strconv.FormatBool(false),
|
||||
bridge.EnableIPMasquerade: strconv.FormatBool(true),
|
||||
}
|
||||
|
||||
n, err := c.NewNetwork("bridge", libnGWNetwork,
|
||||
NetworkOptionGeneric(options.Generic{
|
||||
netlabel.GenericData: netOption,
|
||||
netlabel.EnableIPv6: false,
|
||||
}))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating external connectivity network: %v", err)
|
||||
}
|
||||
return n, err
|
||||
}
|
7
vendor/src/github.com/docker/libnetwork/default_gateway_windows.go
vendored
Normal file
7
vendor/src/github.com/docker/libnetwork/default_gateway_windows.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 windows")
|
||||
}
|
|
@ -1,10 +1,6 @@
|
|||
package driverapi
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
)
|
||||
import "net"
|
||||
|
||||
// NetworkPluginEndpointType represents the Endpoint Type used by Plugin system
|
||||
const NetworkPluginEndpointType = "NetworkDriver"
|
||||
|
@ -14,7 +10,7 @@ type Driver interface {
|
|||
// 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{}) error
|
||||
CreateNetwork(nid string, options map[string]interface{}, ipV4Data, ipV6Data []IPAMData) error
|
||||
|
||||
// DeleteNetwork invokes the driver method to delete network passing
|
||||
// the network id.
|
||||
|
@ -25,7 +21,7 @@ type Driver interface {
|
|||
// specific config. The endpoint information can be either consumed by
|
||||
// the driver or populated by the driver. The config mechanism will
|
||||
// eventually be replaced with labels which are yet to be introduced.
|
||||
CreateEndpoint(nid, eid string, epInfo EndpointInfo, options map[string]interface{}) error
|
||||
CreateEndpoint(nid, eid string, ifInfo InterfaceInfo, options map[string]interface{}) error
|
||||
|
||||
// DeleteEndpoint invokes the driver method to delete an endpoint
|
||||
// passing the network id and endpoint id.
|
||||
|
@ -50,31 +46,26 @@ type Driver interface {
|
|||
Type() string
|
||||
}
|
||||
|
||||
// EndpointInfo provides a go interface to fetch or populate endpoint assigned network resources.
|
||||
type EndpointInfo interface {
|
||||
// Interface returns the interface bound to the endpoint.
|
||||
// If the value is not nil the driver is only expected to consume the interface.
|
||||
// It is an error to try to add interface if the passed down value is non-nil
|
||||
// If the value is nil the driver is expected to add an interface
|
||||
Interface() InterfaceInfo
|
||||
|
||||
// AddInterface is used by the driver to add an interface for the endpoint.
|
||||
// This method will return an error if the driver attempts to add interface
|
||||
// if the Interface() method returned a non-nil value.
|
||||
AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error
|
||||
}
|
||||
|
||||
// InterfaceInfo provides a go interface for drivers to retrive
|
||||
// network information to interface resources.
|
||||
type InterfaceInfo interface {
|
||||
// SetMacAddress allows the driver to set the mac address to the endpoint interface
|
||||
// during the call to CreateEndpoint, if the mac address is not already set.
|
||||
SetMacAddress(mac net.HardwareAddr) error
|
||||
|
||||
// SetIPAddress allows the driver to set the ip address to the endpoint interface
|
||||
// during the call to CreateEndpoint, if the address is not already set.
|
||||
// The API is to be used to assign both the IPv4 and IPv6 address types.
|
||||
SetIPAddress(ip *net.IPNet) error
|
||||
|
||||
// MacAddress returns the MAC address.
|
||||
MacAddress() net.HardwareAddr
|
||||
|
||||
// Address returns the IPv4 address.
|
||||
Address() net.IPNet
|
||||
Address() *net.IPNet
|
||||
|
||||
// AddressIPv6 returns the IPv6 address.
|
||||
AddressIPv6() net.IPNet
|
||||
AddressIPv6() *net.IPNet
|
||||
}
|
||||
|
||||
// InterfaceNameInfo provides a go interface for the drivers to assign names
|
||||
|
@ -110,7 +101,7 @@ type DriverCallback interface {
|
|||
|
||||
// Capability represents the high level capabilities of the drivers which libnetwork can make use of
|
||||
type Capability struct {
|
||||
DataScope datastore.DataScope
|
||||
DataScope string
|
||||
}
|
||||
|
||||
// DiscoveryType represents the type of discovery element the DiscoverNew function is invoked on
|
||||
|
@ -126,3 +117,13 @@ type NodeDiscoveryData struct {
|
|||
Address string
|
||||
Self bool
|
||||
}
|
||||
|
||||
// IPAMData represents the per-network ip related
|
||||
// operational information libnetwork will send
|
||||
// to the network driver during CreateNetwork()
|
||||
type IPAMData struct {
|
||||
AddressSpace string
|
||||
Pool *net.IPNet
|
||||
Gateway *net.IPNet
|
||||
AuxAddresses map[string]*net.IPNet
|
||||
}
|
||||
|
|
103
vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go
vendored
Normal file
103
vendor/src/github.com/docker/libnetwork/driverapi/ipamdata.go
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package driverapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// MarshalJSON encodes IPAMData into json message
|
||||
func (i *IPAMData) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{}
|
||||
m["AddressSpace"] = i.AddressSpace
|
||||
if i.Pool != nil {
|
||||
m["Pool"] = i.Pool.String()
|
||||
}
|
||||
if i.Gateway != nil {
|
||||
m["Gateway"] = i.Gateway.String()
|
||||
}
|
||||
if i.AuxAddresses != nil {
|
||||
am := make(map[string]string, len(i.AuxAddresses))
|
||||
for k, v := range i.AuxAddresses {
|
||||
am[k] = v.String()
|
||||
}
|
||||
m["AuxAddresses"] = am
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a json message into IPAMData
|
||||
func (i *IPAMData) UnmarshalJSON(data []byte) error {
|
||||
var (
|
||||
m map[string]interface{}
|
||||
err error
|
||||
)
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
i.AddressSpace = m["AddressSpace"].(string)
|
||||
if v, ok := m["Pool"]; ok {
|
||||
if i.Pool, err = types.ParseCIDR(v.(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v, ok := m["Gateway"]; ok {
|
||||
if i.Gateway, err = types.ParseCIDR(v.(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if v, ok := m["AuxAddresses"]; ok {
|
||||
b, _ := json.Marshal(v)
|
||||
var am map[string]string
|
||||
if err = json.Unmarshal(b, &am); err != nil {
|
||||
return err
|
||||
}
|
||||
i.AuxAddresses = make(map[string]*net.IPNet, len(am))
|
||||
for k, v := range am {
|
||||
if i.AuxAddresses[k], err = types.ParseCIDR(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks wheter the IPAMData structure contains congruent data
|
||||
func (i *IPAMData) Validate() error {
|
||||
var isV6 bool
|
||||
if i.Pool == nil {
|
||||
return types.BadRequestErrorf("invalid pool")
|
||||
}
|
||||
if i.Gateway == nil {
|
||||
return types.BadRequestErrorf("invalid gateway address")
|
||||
}
|
||||
isV6 = i.IsV6()
|
||||
if isV6 && i.Gateway.IP.To4() != nil || !isV6 && i.Gateway.IP.To4() == nil {
|
||||
return types.BadRequestErrorf("incongruent ip versions for pool and gateway")
|
||||
}
|
||||
for k, sip := range i.AuxAddresses {
|
||||
if isV6 && sip.IP.To4() != nil || !isV6 && sip.IP.To4() == nil {
|
||||
return types.BadRequestErrorf("incongruent ip versions for pool and secondary ip address %s", k)
|
||||
}
|
||||
}
|
||||
if !i.Pool.Contains(i.Gateway.IP) {
|
||||
return types.BadRequestErrorf("invalid gateway address (%s) does not belong to the pool (%s)", i.Gateway, i.Pool)
|
||||
}
|
||||
for k, sip := range i.AuxAddresses {
|
||||
if !i.Pool.Contains(sip.IP) {
|
||||
return types.BadRequestErrorf("invalid secondary address %s (%s) does not belong to the pool (%s)", k, i.Gateway, i.Pool)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsV6 returns wheter this is an IPv6 IPAMData structure
|
||||
func (i *IPAMData) IsV6() bool {
|
||||
return nil == i.Pool.IP.To4()
|
||||
}
|
||||
|
||||
func (i *IPAMData) String() string {
|
||||
return fmt.Sprintf("AddressSpace: %s\nPool: %v\nGateway: %v\nAddresses: %v", i.AddressSpace, i.Pool, i.Gateway, i.AuxAddresses)
|
||||
}
|
|
@ -4,6 +4,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
|
||||
remoteIpam "github.com/docker/libnetwork/ipams/remote"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
)
|
||||
|
||||
|
@ -29,11 +32,6 @@ func makeDriverConfig(c *controller, ntype string) map[string]interface{} {
|
|||
|
||||
config := make(map[string]interface{})
|
||||
|
||||
if c.validateGlobalStoreConfig() {
|
||||
config[netlabel.KVProvider] = c.cfg.GlobalStore.Client.Provider
|
||||
config[netlabel.KVProviderURL] = c.cfg.GlobalStore.Client.Address
|
||||
}
|
||||
|
||||
for _, label := range c.cfg.Daemon.Labels {
|
||||
if !strings.HasPrefix(netlabel.Key(label), netlabel.DriverPrefix+"."+ntype) {
|
||||
continue
|
||||
|
@ -43,13 +41,38 @@ func makeDriverConfig(c *controller, ntype string) map[string]interface{} {
|
|||
}
|
||||
|
||||
drvCfg, ok := c.cfg.Daemon.DriverCfg[ntype]
|
||||
if !ok {
|
||||
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 drvCfg.(map[string]interface{}) {
|
||||
config[k] = v
|
||||
for k, v := range c.cfg.Scopes {
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
config[netlabel.MakeKVProvider(k)] = v.Client.Provider
|
||||
config[netlabel.MakeKVProviderURL(k)] = v.Client.Address
|
||||
config[netlabel.MakeKVProviderConfig(k)] = 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,
|
||||
} {
|
||||
if err := fn(ic, lDs, gDs); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipallocator"
|
||||
"github.com/docker/libnetwork/iptables"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
|
@ -35,8 +34,11 @@ const (
|
|||
maxAllocatePortAttempts = 10
|
||||
)
|
||||
|
||||
var (
|
||||
ipAllocator *ipallocator.IPAllocator
|
||||
const (
|
||||
// DefaultGatewayV4AuxKey represents the default-gateway configured by the user
|
||||
DefaultGatewayV4AuxKey = "DefaultGatewayIPv4"
|
||||
// DefaultGatewayV6AuxKey represents the ipv6 default-gateway configured by the user
|
||||
DefaultGatewayV6AuxKey = "DefaultGatewayIPv6"
|
||||
)
|
||||
|
||||
// configuration info for the "bridge" driver.
|
||||
|
@ -48,18 +50,21 @@ type configuration struct {
|
|||
|
||||
// networkConfiguration for network specific configuration
|
||||
type networkConfiguration struct {
|
||||
ID string
|
||||
BridgeName string
|
||||
AddressIPv4 *net.IPNet
|
||||
FixedCIDR *net.IPNet
|
||||
FixedCIDRv6 *net.IPNet
|
||||
EnableIPv6 bool
|
||||
EnableIPMasquerade bool
|
||||
EnableICC bool
|
||||
Mtu int
|
||||
DefaultGatewayIPv4 net.IP
|
||||
DefaultGatewayIPv6 net.IP
|
||||
DefaultBindingIP net.IP
|
||||
DefaultBridge bool
|
||||
// Internal fields set after ipam data parsing
|
||||
AddressIPv4 *net.IPNet
|
||||
AddressIPv6 *net.IPNet
|
||||
DefaultGatewayIPv4 net.IP
|
||||
DefaultGatewayIPv6 net.IP
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
}
|
||||
|
||||
// endpointConfiguration represents the user specified configuration for the sandbox endpoint
|
||||
|
@ -102,12 +107,12 @@ type driver struct {
|
|||
natChain *iptables.ChainInfo
|
||||
filterChain *iptables.ChainInfo
|
||||
networks map[string]*bridgeNetwork
|
||||
store datastore.DataStore
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// New constructs a new bridge driver
|
||||
func newDriver() *driver {
|
||||
ipAllocator = ipallocator.New()
|
||||
return &driver{networks: map[string]*bridgeNetwork{}, config: &configuration{}}
|
||||
}
|
||||
|
||||
|
@ -148,19 +153,6 @@ func (c *networkConfiguration) Validate() error {
|
|||
|
||||
// If bridge v4 subnet is specified
|
||||
if c.AddressIPv4 != nil {
|
||||
// If Container restricted subnet is specified, it must be a subset of bridge subnet
|
||||
if c.FixedCIDR != nil {
|
||||
// Check Network address
|
||||
if !c.AddressIPv4.Contains(c.FixedCIDR.IP) {
|
||||
return &ErrInvalidContainerSubnet{}
|
||||
}
|
||||
// Check it is effectively a subset
|
||||
brNetLen, _ := c.AddressIPv4.Mask.Size()
|
||||
cnNetLen, _ := c.FixedCIDR.Mask.Size()
|
||||
if brNetLen > cnNetLen {
|
||||
return &ErrInvalidContainerSubnet{}
|
||||
}
|
||||
}
|
||||
// If default gw is specified, it must be part of bridge subnet
|
||||
if c.DefaultGatewayIPv4 != nil {
|
||||
if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) {
|
||||
|
@ -169,167 +161,81 @@ func (c *networkConfiguration) Validate() error {
|
|||
}
|
||||
}
|
||||
|
||||
// If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet
|
||||
// If default v6 gw is specified, AddressIPv6 must be specified and gw must belong to AddressIPv6 subnet
|
||||
if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil {
|
||||
if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) {
|
||||
if c.AddressIPv6 == nil || !c.AddressIPv6.Contains(c.DefaultGatewayIPv6) {
|
||||
return &ErrInvalidGateway{}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Conflicts check if two NetworkConfiguration objects overlap
|
||||
func (c *networkConfiguration) Conflicts(o *networkConfiguration) bool {
|
||||
func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
|
||||
if o == nil {
|
||||
return false
|
||||
return fmt.Errorf("same configuration")
|
||||
}
|
||||
|
||||
// Also empty, becasue only one network with empty name is allowed
|
||||
if c.BridgeName == o.BridgeName {
|
||||
return true
|
||||
return fmt.Errorf("networks have same bridge name")
|
||||
}
|
||||
|
||||
// They must be in different subnets
|
||||
if (c.AddressIPv4 != nil && o.AddressIPv4 != nil) &&
|
||||
(c.AddressIPv4.Contains(o.AddressIPv4.IP) || o.AddressIPv4.Contains(c.AddressIPv4.IP)) {
|
||||
return true
|
||||
return fmt.Errorf("networks have overlapping IPv4")
|
||||
}
|
||||
|
||||
return false
|
||||
// They must be in different v6 subnets
|
||||
if (c.AddressIPv6 != nil && o.AddressIPv6 != nil) &&
|
||||
(c.AddressIPv6.Contains(o.AddressIPv6.IP) || o.AddressIPv6.Contains(c.AddressIPv6.IP)) {
|
||||
return fmt.Errorf("networks have overlapping IPv6")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fromMap retrieve the configuration data from the map form.
|
||||
func (c *networkConfiguration) fromMap(data map[string]interface{}) error {
|
||||
func (c *networkConfiguration) fromLabels(labels map[string]string) error {
|
||||
var err error
|
||||
|
||||
if i, ok := data["BridgeName"]; ok && i != nil {
|
||||
if c.BridgeName, ok = i.(string); !ok {
|
||||
return types.BadRequestErrorf("invalid type for BridgeName value")
|
||||
for label, value := range labels {
|
||||
switch label {
|
||||
case BridgeName:
|
||||
c.BridgeName = value
|
||||
case netlabel.DriverMTU:
|
||||
if c.Mtu, err = strconv.Atoi(value); err != nil {
|
||||
return parseErr(label, value, err.Error())
|
||||
}
|
||||
case netlabel.EnableIPv6:
|
||||
if c.EnableIPv6, err = strconv.ParseBool(value); err != nil {
|
||||
return parseErr(label, value, err.Error())
|
||||
}
|
||||
case EnableIPMasquerade:
|
||||
if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil {
|
||||
return parseErr(label, value, err.Error())
|
||||
}
|
||||
case EnableICC:
|
||||
if c.EnableICC, err = strconv.ParseBool(value); err != nil {
|
||||
return parseErr(label, value, err.Error())
|
||||
}
|
||||
case DefaultBridge:
|
||||
if c.DefaultBridge, err = strconv.ParseBool(value); err != nil {
|
||||
return parseErr(label, value, err.Error())
|
||||
}
|
||||
case DefaultBindingIP:
|
||||
if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil {
|
||||
return parseErr(label, value, "nil ip")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["Mtu"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.Mtu, err = strconv.Atoi(s); err != nil {
|
||||
return types.BadRequestErrorf("failed to parse Mtu value: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for Mtu value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["EnableIPv6"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.EnableIPv6, err = strconv.ParseBool(s); err != nil {
|
||||
return types.BadRequestErrorf("failed to parse EnableIPv6 value: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for EnableIPv6 value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["EnableIPMasquerade"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.EnableIPMasquerade, err = strconv.ParseBool(s); err != nil {
|
||||
return types.BadRequestErrorf("failed to parse EnableIPMasquerade value: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for EnableIPMasquerade value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["EnableICC"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.EnableICC, err = strconv.ParseBool(s); err != nil {
|
||||
return types.BadRequestErrorf("failed to parse EnableICC value: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for EnableICC value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["DefaultBridge"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.DefaultBridge, err = strconv.ParseBool(s); err != nil {
|
||||
return types.BadRequestErrorf("failed to parse DefaultBridge value: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for DefaultBridge value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["AddressIPv4"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if ip, nw, e := net.ParseCIDR(s); e == nil {
|
||||
nw.IP = ip
|
||||
c.AddressIPv4 = nw
|
||||
} else {
|
||||
return types.BadRequestErrorf("failed to parse AddressIPv4 value")
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for AddressIPv4 value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["FixedCIDR"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if ip, nw, e := net.ParseCIDR(s); e == nil {
|
||||
nw.IP = ip
|
||||
c.FixedCIDR = nw
|
||||
} else {
|
||||
return types.BadRequestErrorf("failed to parse FixedCIDR value")
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for FixedCIDR value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["FixedCIDRv6"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if ip, nw, e := net.ParseCIDR(s); e == nil {
|
||||
nw.IP = ip
|
||||
c.FixedCIDRv6 = nw
|
||||
} else {
|
||||
return types.BadRequestErrorf("failed to parse FixedCIDRv6 value")
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for FixedCIDRv6 value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["DefaultGatewayIPv4"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.DefaultGatewayIPv4 = net.ParseIP(s); c.DefaultGatewayIPv4 == nil {
|
||||
return types.BadRequestErrorf("failed to parse DefaultGatewayIPv4 value")
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for DefaultGatewayIPv4 value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["DefaultGatewayIPv6"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.DefaultGatewayIPv6 = net.ParseIP(s); c.DefaultGatewayIPv6 == nil {
|
||||
return types.BadRequestErrorf("failed to parse DefaultGatewayIPv6 value")
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for DefaultGatewayIPv6 value")
|
||||
}
|
||||
}
|
||||
|
||||
if i, ok := data["DefaultBindingIP"]; ok && i != nil {
|
||||
if s, ok := i.(string); ok {
|
||||
if c.DefaultBindingIP = net.ParseIP(s); c.DefaultBindingIP == nil {
|
||||
return types.BadRequestErrorf("failed to parse DefaultBindingIP value")
|
||||
}
|
||||
} else {
|
||||
return types.BadRequestErrorf("invalid type for DefaultBindingIP value")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseErr(label, value, errString string) error {
|
||||
return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString)
|
||||
}
|
||||
|
||||
func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, error) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
@ -435,6 +341,11 @@ func (d *driver) configure(option map[string]interface{}) error {
|
|||
var config *configuration
|
||||
var err error
|
||||
|
||||
err = d.initStore(option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
|
@ -498,12 +409,12 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error)
|
|||
switch opt := data.(type) {
|
||||
case *networkConfiguration:
|
||||
config = opt
|
||||
case map[string]interface{}:
|
||||
case map[string]string:
|
||||
config = &networkConfiguration{
|
||||
EnableICC: true,
|
||||
EnableIPMasquerade: true,
|
||||
}
|
||||
err = config.fromMap(opt)
|
||||
err = config.fromLabels(opt)
|
||||
case options.Generic:
|
||||
var opaqueConfig interface{}
|
||||
if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
|
||||
|
@ -516,9 +427,41 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error)
|
|||
return config, err
|
||||
}
|
||||
|
||||
func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
||||
if len(ipamV4Data) > 1 || len(ipamV6Data) > 1 {
|
||||
return types.ForbiddenErrorf("bridge driver doesnt support multiple subnets")
|
||||
}
|
||||
|
||||
if len(ipamV4Data) == 0 {
|
||||
return types.BadRequestErrorf("bridge network %s requires ipv4 configuration", id)
|
||||
}
|
||||
|
||||
if ipamV4Data[0].Gateway != nil {
|
||||
c.AddressIPv4 = types.GetIPNetCopy(ipamV4Data[0].Gateway)
|
||||
}
|
||||
|
||||
if gw, ok := ipamV4Data[0].AuxAddresses[DefaultGatewayV4AuxKey]; ok {
|
||||
c.DefaultGatewayIPv4 = gw.IP
|
||||
}
|
||||
|
||||
if len(ipamV6Data) > 0 {
|
||||
if ipamV6Data[0].Gateway != nil {
|
||||
c.AddressIPv6 = types.GetIPNetCopy(ipamV6Data[0].Gateway)
|
||||
}
|
||||
|
||||
if gw, ok := ipamV6Data[0].AuxAddresses[DefaultGatewayV6AuxKey]; ok {
|
||||
c.DefaultGatewayIPv6 = gw.IP
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) {
|
||||
var err error
|
||||
config := &networkConfiguration{}
|
||||
var (
|
||||
err error
|
||||
config = &networkConfiguration{}
|
||||
)
|
||||
|
||||
// Parse generic label first, config will be re-assigned
|
||||
if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
|
||||
|
@ -528,8 +471,8 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
|
|||
}
|
||||
|
||||
// Process well-known labels next
|
||||
if _, ok := option[netlabel.EnableIPv6]; ok {
|
||||
config.EnableIPv6 = option[netlabel.EnableIPv6].(bool)
|
||||
if val, ok := option[netlabel.EnableIPv6]; ok {
|
||||
config.EnableIPv6 = val.(bool)
|
||||
}
|
||||
|
||||
// Finally validate the configuration
|
||||
|
@ -540,15 +483,16 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
|
|||
if config.BridgeName == "" && config.DefaultBridge == false {
|
||||
config.BridgeName = "br-" + id[:12]
|
||||
}
|
||||
|
||||
config.ID = id
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// Returns the non link-local IPv6 subnet for the containers attached to this bridge if found, nil otherwise
|
||||
func getV6Network(config *networkConfiguration, i *bridgeInterface) *net.IPNet {
|
||||
if config.FixedCIDRv6 != nil {
|
||||
return config.FixedCIDRv6
|
||||
if config.AddressIPv6 != nil {
|
||||
return config.AddressIPv6
|
||||
}
|
||||
|
||||
if i.bridgeIPv6 != nil && i.bridgeIPv6.IP != nil && !i.bridgeIPv6.IP.IsLinkLocalUnicast() {
|
||||
return i.bridgeIPv6
|
||||
}
|
||||
|
@ -569,11 +513,7 @@ func (d *driver) getNetworks() []*bridgeNetwork {
|
|||
}
|
||||
|
||||
// Create a new network using bridge plugin
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
||||
var err error
|
||||
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
// Sanity checks
|
||||
d.Lock()
|
||||
if _, ok := d.networks[id]; ok {
|
||||
|
@ -587,19 +527,38 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = config.processIPAM(id, ipV4Data, ipV6Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = d.createNetwork(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.storeUpdate(config)
|
||||
}
|
||||
|
||||
func (d *driver) createNetwork(config *networkConfiguration) error {
|
||||
var err error
|
||||
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
networkList := d.getNetworks()
|
||||
for _, nw := range networkList {
|
||||
nw.Lock()
|
||||
nwConfig := nw.config
|
||||
nw.Unlock()
|
||||
if nwConfig.Conflicts(config) {
|
||||
return types.ForbiddenErrorf("conflicts with network %s (%s)", nw.id, nw.config.BridgeName)
|
||||
if err := nwConfig.Conflicts(config); err != nil {
|
||||
return types.ForbiddenErrorf("cannot create network %s (%s): conflicts with network %s (%s): %s",
|
||||
nwConfig.BridgeName, config.ID, nw.id, nw.config.BridgeName, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Create and set network handler in driver
|
||||
network := &bridgeNetwork{
|
||||
id: id,
|
||||
id: config.ID,
|
||||
endpoints: make(map[string]*bridgeEndpoint),
|
||||
config: config,
|
||||
portMapper: portmapper.New(),
|
||||
|
@ -607,14 +566,14 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
}
|
||||
|
||||
d.Lock()
|
||||
d.networks[id] = network
|
||||
d.networks[config.ID] = network
|
||||
d.Unlock()
|
||||
|
||||
// On failure make sure to reset driver network handler to nil
|
||||
defer func() {
|
||||
if err != nil {
|
||||
d.Lock()
|
||||
delete(d.networks, id)
|
||||
delete(d.networks, config.ID)
|
||||
d.Unlock()
|
||||
}
|
||||
}()
|
||||
|
@ -627,7 +586,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
// networks. This step is needed now because driver might have now set the bridge
|
||||
// name on this config struct. And because we need to check for possible address
|
||||
// conflicts, so we need to check against operationa lnetworks.
|
||||
if err = config.conflictsWithNetworks(id, networkList); err != nil {
|
||||
if err = config.conflictsWithNetworks(config.ID, networkList); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -654,10 +613,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
// Even if a bridge exists try to setup IPv4.
|
||||
bridgeSetup.queueStep(setupBridgeIPv4)
|
||||
|
||||
enableIPv6Forwarding := false
|
||||
if d.config.EnableIPForwarding && config.FixedCIDRv6 != nil {
|
||||
enableIPv6Forwarding = true
|
||||
}
|
||||
enableIPv6Forwarding := d.config.EnableIPForwarding && config.AddressIPv6 != nil
|
||||
|
||||
// Conditionally queue setup steps depending on configuration values.
|
||||
for _, step := range []struct {
|
||||
|
@ -674,14 +630,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
// the case of a previously existing device.
|
||||
{bridgeAlreadyExists, setupVerifyAndReconcile},
|
||||
|
||||
// Setup the bridge to allocate containers IPv4 addresses in the
|
||||
// specified subnet.
|
||||
{config.FixedCIDR != nil, setupFixedCIDRv4},
|
||||
|
||||
// Setup the bridge to allocate containers global IPv6 addresses in the
|
||||
// specified subnet.
|
||||
{config.FixedCIDRv6 != nil, setupFixedCIDRv6},
|
||||
|
||||
// Enable IPv6 Forwarding
|
||||
{enableIPv6Forwarding, setupIPv6Forwarding},
|
||||
|
||||
|
@ -712,8 +660,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
// Block bridge IP from being allocated.
|
||||
bridgeSetup.queueStep(allocateBridgeIP)
|
||||
// Apply the prepared list of steps, and abort at the first error.
|
||||
bridgeSetup.queueStep(setupDeviceUp)
|
||||
if err = bridgeSetup.apply(); err != nil {
|
||||
|
@ -741,10 +687,6 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
config := n.config
|
||||
n.Unlock()
|
||||
|
||||
if config.BridgeName == DefaultBridgeName {
|
||||
return types.ForbiddenErrorf("default network of type \"%s\" cannot be deleted", networkType)
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
delete(d.networks, nid)
|
||||
d.Unlock()
|
||||
|
@ -789,22 +731,12 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Programming
|
||||
// We only delete the bridge when it's not the default bridge. This is keep the backward compatible behavior.
|
||||
if !config.DefaultBridge {
|
||||
err = netlink.LinkDel(n.bridge.Link)
|
||||
|
||||
// Release ip addresses (ignore errors)
|
||||
if config.FixedCIDR == nil || config.FixedCIDR.Contains(config.DefaultGatewayIPv4) {
|
||||
if e := ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, n.bridge.gatewayIPv4); e != nil {
|
||||
logrus.Warnf("Failed to release default gateway address %s: %v", n.bridge.gatewayIPv4.String(), e)
|
||||
}
|
||||
}
|
||||
if config.FixedCIDR == nil || config.FixedCIDR.Contains(n.bridge.bridgeIPv4.IP) {
|
||||
if e := ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, n.bridge.bridgeIPv4.IP); e != nil {
|
||||
logrus.Warnf("Failed to release bridge IP %s: %v", n.bridge.bridgeIPv4.IP.String(), e)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
return d.storeDelete(config)
|
||||
}
|
||||
|
||||
func addToBridge(ifaceName, bridgeName string) error {
|
||||
|
@ -861,20 +793,11 @@ func setHairpinMode(link netlink.Link, enable bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
|
||||
var (
|
||||
ipv6Addr *net.IPNet
|
||||
err error
|
||||
)
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
||||
defer osl.InitOSContext()()
|
||||
|
||||
if epInfo == nil {
|
||||
return errors.New("invalid endpoint info passed")
|
||||
}
|
||||
|
||||
if epInfo.Interface() != nil {
|
||||
return errors.New("non-nil interface passed to bridge(local) driver")
|
||||
if ifInfo == nil {
|
||||
return errors.New("invalid interface info passed")
|
||||
}
|
||||
|
||||
// Get the network handler and make sure it exists
|
||||
|
@ -1000,12 +923,11 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
|
|||
}
|
||||
}
|
||||
|
||||
// v4 address for the sandbox side pipe interface
|
||||
ip4, err := ipAllocator.RequestIP(n.bridge.bridgeIPv4, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
|
||||
// Create the sandbox side pipe interface
|
||||
endpoint.srcName = containerIfName
|
||||
endpoint.macAddress = ifInfo.MacAddress()
|
||||
endpoint.addr = ifInfo.Address()
|
||||
endpoint.addrv6 = ifInfo.AddressIPv6()
|
||||
|
||||
// Down the interface before configuring mac address.
|
||||
if err = netlink.LinkSetDown(sbox); err != nil {
|
||||
|
@ -1013,56 +935,38 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
|
|||
}
|
||||
|
||||
// Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP.
|
||||
mac := electMacAddress(epConfig, ip4)
|
||||
err = netlink.LinkSetHardwareAddr(sbox, mac)
|
||||
if endpoint.macAddress == nil {
|
||||
endpoint.macAddress = electMacAddress(epConfig, endpoint.addr.IP)
|
||||
if err := ifInfo.SetMacAddress(endpoint.macAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = netlink.LinkSetHardwareAddr(sbox, endpoint.macAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err)
|
||||
}
|
||||
endpoint.macAddress = mac
|
||||
|
||||
// Up the host interface after finishing all netlink configuration
|
||||
if err = netlink.LinkSetUp(host); err != nil {
|
||||
return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
|
||||
}
|
||||
|
||||
// v6 address for the sandbox side pipe interface
|
||||
ipv6Addr = &net.IPNet{}
|
||||
if config.EnableIPv6 {
|
||||
if endpoint.addrv6 == nil && config.EnableIPv6 {
|
||||
var ip6 net.IP
|
||||
|
||||
network := n.bridge.bridgeIPv6
|
||||
if config.FixedCIDRv6 != nil {
|
||||
network = config.FixedCIDRv6
|
||||
}
|
||||
|
||||
ones, _ := network.Mask.Size()
|
||||
if ones <= 80 {
|
||||
ip6 = make(net.IP, len(network.IP))
|
||||
copy(ip6, network.IP)
|
||||
for i, h := range mac {
|
||||
for i, h := range endpoint.macAddress {
|
||||
ip6[i+10] = h
|
||||
}
|
||||
}
|
||||
|
||||
ip6, err := ipAllocator.RequestIP(network, ip6)
|
||||
if err != nil {
|
||||
endpoint.addrv6 = &net.IPNet{IP: ip6, Mask: network.Mask}
|
||||
if err := ifInfo.SetIPAddress(endpoint.addrv6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ipv6Addr = &net.IPNet{IP: ip6, Mask: network.Mask}
|
||||
}
|
||||
|
||||
// Create the sandbox side pipe interface
|
||||
endpoint.srcName = containerIfName
|
||||
endpoint.addr = ipv4Addr
|
||||
|
||||
if config.EnableIPv6 {
|
||||
endpoint.addrv6 = ipv6Addr
|
||||
}
|
||||
|
||||
err = epInfo.AddInterface(endpoint.macAddress, *ipv4Addr, *ipv6Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Program any required port mapping and store them in the endpoint
|
||||
|
@ -1128,28 +1032,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
|
|||
// Remove port mappings. Do not stop endpoint delete on unmap failure
|
||||
n.releasePorts(ep)
|
||||
|
||||
// Release the v4 address allocated to this endpoint's sandbox interface
|
||||
err = ipAllocator.ReleaseIP(n.bridge.bridgeIPv4, ep.addr.IP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
config := n.config
|
||||
n.Unlock()
|
||||
|
||||
// Release the v6 address allocated to this endpoint's sandbox interface
|
||||
if config.EnableIPv6 {
|
||||
network := n.bridge.bridgeIPv6
|
||||
if config.FixedCIDRv6 != nil {
|
||||
network = config.FixedCIDRv6
|
||||
}
|
||||
err := ipAllocator.ReleaseIP(network, ep.addrv6.IP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Try removal of link. Discard error: link pair might have
|
||||
// already been deleted by sandbox delete. Make sure defer
|
||||
// does not see this error either.
|
||||
|
|
211
vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go
vendored
Normal file
211
vendor/src/github.com/docker/libnetwork/drivers/bridge/bridge_store.go
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libkv/store/boltdb"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const bridgePrefix = "bridge"
|
||||
|
||||
func (d *driver) initStore(option map[string]interface{}) error {
|
||||
var err error
|
||||
|
||||
provider, provOk := option[netlabel.LocalKVProvider]
|
||||
provURL, urlOk := option[netlabel.LocalKVProviderURL]
|
||||
|
||||
if provOk && urlOk {
|
||||
cfg := &datastore.ScopeCfg{
|
||||
Client: datastore.ScopeClientCfg{
|
||||
Provider: provider.(string),
|
||||
Address: provURL.(string),
|
||||
},
|
||||
}
|
||||
|
||||
provConfig, confOk := option[netlabel.LocalKVProviderConfig]
|
||||
if confOk {
|
||||
cfg.Client.Config = provConfig.(*store.Config)
|
||||
}
|
||||
|
||||
d.store, err = datastore.NewDataStore(datastore.LocalScope, cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bridge driver failed to initialize data store: %v", err)
|
||||
}
|
||||
|
||||
return d.populateNetworks()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) populateNetworks() error {
|
||||
kvol, err := d.store.List(datastore.Key(bridgePrefix), &networkConfiguration{})
|
||||
if err != nil && err != datastore.ErrKeyNotFound && err != boltdb.ErrBoltBucketNotFound {
|
||||
return fmt.Errorf("failed to get bridge network configurations from store: %v", err)
|
||||
}
|
||||
|
||||
// It's normal for network configuration state to be empty. Just return.
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, kvo := range kvol {
|
||||
ncfg := kvo.(*networkConfiguration)
|
||||
if err = d.createNetwork(ncfg); err != nil {
|
||||
logrus.Warnf("could not create bridge network for id %s bridge name %s while booting up from persistent state", ncfg.ID, ncfg.BridgeName)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) storeUpdate(kvObject datastore.KVObject) error {
|
||||
if d.store == nil {
|
||||
logrus.Warnf("bridge store not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := d.store.PutObjectAtomic(kvObject); err != nil {
|
||||
return fmt.Errorf("failed to update bridge store for object type %T: %v", kvObject, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) storeDelete(kvObject datastore.KVObject) error {
|
||||
if d.store == nil {
|
||||
logrus.Debugf("bridge store not initialized. kv object %s is not deleted from store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
|
||||
retry:
|
||||
if err := d.store.DeleteObjectAtomic(kvObject); err != nil {
|
||||
if err == datastore.ErrKeyModified {
|
||||
if err := d.store.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil {
|
||||
return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err)
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) {
|
||||
nMap := make(map[string]interface{})
|
||||
nMap["ID"] = ncfg.ID
|
||||
nMap["BridgeName"] = ncfg.BridgeName
|
||||
nMap["EnableIPv6"] = ncfg.EnableIPv6
|
||||
nMap["EnableIPMasquerade"] = ncfg.EnableIPMasquerade
|
||||
nMap["EnableICC"] = ncfg.EnableICC
|
||||
nMap["Mtu"] = ncfg.Mtu
|
||||
nMap["DefaultBridge"] = ncfg.DefaultBridge
|
||||
nMap["DefaultBindingIP"] = ncfg.DefaultBindingIP.String()
|
||||
nMap["DefaultGatewayIPv4"] = ncfg.DefaultGatewayIPv4.String()
|
||||
nMap["DefaultGatewayIPv6"] = ncfg.DefaultGatewayIPv6.String()
|
||||
|
||||
if ncfg.AddressIPv4 != nil {
|
||||
nMap["AddressIPv4"] = ncfg.AddressIPv4.String()
|
||||
}
|
||||
|
||||
if ncfg.AddressIPv6 != nil {
|
||||
nMap["AddressIPv6"] = ncfg.AddressIPv6.String()
|
||||
}
|
||||
|
||||
return json.Marshal(nMap)
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error {
|
||||
var (
|
||||
err error
|
||||
nMap map[string]interface{}
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(b, &nMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v, ok := nMap["AddressIPv4"]; ok {
|
||||
if ncfg.AddressIPv4, err = types.ParseCIDR(v.(string)); err != nil {
|
||||
return types.InternalErrorf("failed to decode bridge network address IPv4 after json unmarshal: %s", v.(string))
|
||||
}
|
||||
}
|
||||
|
||||
if v, ok := nMap["AddressIPv6"]; ok {
|
||||
if ncfg.AddressIPv6, err = types.ParseCIDR(v.(string)); err != nil {
|
||||
return types.InternalErrorf("failed to decode bridge network address IPv6 after json unmarshal: %s", v.(string))
|
||||
}
|
||||
}
|
||||
|
||||
ncfg.DefaultBridge = nMap["DefaultBridge"].(bool)
|
||||
ncfg.DefaultBindingIP = net.ParseIP(nMap["DefaultBindingIP"].(string))
|
||||
ncfg.DefaultGatewayIPv4 = net.ParseIP(nMap["DefaultGatewayIPv4"].(string))
|
||||
ncfg.DefaultGatewayIPv6 = net.ParseIP(nMap["DefaultGatewayIPv6"].(string))
|
||||
ncfg.ID = nMap["ID"].(string)
|
||||
ncfg.BridgeName = nMap["BridgeName"].(string)
|
||||
ncfg.EnableIPv6 = nMap["EnableIPv6"].(bool)
|
||||
ncfg.EnableIPMasquerade = nMap["EnableIPMasquerade"].(bool)
|
||||
ncfg.EnableICC = nMap["EnableICC"].(bool)
|
||||
ncfg.Mtu = int(nMap["Mtu"].(float64))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) Key() []string {
|
||||
return []string{bridgePrefix, ncfg.ID}
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) KeyPrefix() []string {
|
||||
return []string{bridgePrefix}
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) Value() []byte {
|
||||
b, err := json.Marshal(ncfg)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) SetValue(value []byte) error {
|
||||
return json.Unmarshal(value, ncfg)
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) Index() uint64 {
|
||||
return ncfg.dbIndex
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) SetIndex(index uint64) {
|
||||
ncfg.dbIndex = index
|
||||
ncfg.dbExists = true
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) Exists() bool {
|
||||
return ncfg.dbExists
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) Skip() bool {
|
||||
return ncfg.DefaultBridge
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) New() datastore.KVObject {
|
||||
return &networkConfiguration{}
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) CopyTo(o datastore.KVObject) error {
|
||||
dstNcfg := o.(*networkConfiguration)
|
||||
*dstNcfg = *ncfg
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ncfg *networkConfiguration) DataScope() string {
|
||||
return datastore.LocalScope
|
||||
}
|
18
vendor/src/github.com/docker/libnetwork/drivers/bridge/labels.go
vendored
Normal file
18
vendor/src/github.com/docker/libnetwork/drivers/bridge/labels.go
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
package bridge
|
||||
|
||||
const (
|
||||
// BridgeName label for bridge driver
|
||||
BridgeName = "com.docker.network.bridge.name"
|
||||
|
||||
// EnableIPMasquerade label for bridge driver
|
||||
EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade"
|
||||
|
||||
// EnableICC label
|
||||
EnableICC = "com.docker.network.bridge.enable_icc"
|
||||
|
||||
// DefaultBindingIP label
|
||||
DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4"
|
||||
|
||||
// DefaultBridge label
|
||||
DefaultBridge = "com.docker.network.bridge.default_bridge"
|
||||
)
|
|
@ -22,7 +22,7 @@ const (
|
|||
//Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] )
|
||||
func getIPVersion(config *networkConfiguration) ipVersion {
|
||||
ipVersion := ipv4
|
||||
if config.FixedCIDRv6 != nil || config.EnableIPv6 {
|
||||
if config.AddressIPv6 != nil || config.EnableIPv6 {
|
||||
ipVersion |= ipv6
|
||||
}
|
||||
return ipVersion
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
func setupFixedCIDRv4(config *networkConfiguration, i *bridgeInterface) error {
|
||||
addrv4, _, err := i.addresses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR)
|
||||
if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil {
|
||||
return &FixedCIDRv4Error{Subnet: config.FixedCIDR, Net: addrv4.IPNet, Err: err}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package bridge
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func setupFixedCIDRv6(config *networkConfiguration, i *bridgeInterface) error {
|
||||
log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6)
|
||||
if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil {
|
||||
return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err}
|
||||
}
|
||||
|
||||
// Setting route to global IPv6 subnet
|
||||
log.Debugf("Adding route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
|
||||
err := netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: i.Link.Attrs().Index,
|
||||
Dst: config.FixedCIDRv6,
|
||||
})
|
||||
if err != nil && !os.IsExist(err) {
|
||||
log.Errorf("Could not add route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -3,129 +3,43 @@ package bridge
|
|||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
var bridgeNetworks []*net.IPNet
|
||||
|
||||
func init() {
|
||||
// Here we don't follow the convention of using the 1st IP of the range for the gateway.
|
||||
// This is to use the same gateway IPs as the /24 ranges, which predate the /16 ranges.
|
||||
// In theory this shouldn't matter - in practice there's bound to be a few scripts relying
|
||||
// on the internal addressing or other stupid things like that.
|
||||
// They shouldn't, but hey, let's not break them unless we really have to.
|
||||
// Don't use 172.16.0.0/16, it conflicts with EC2 DNS 172.16.0.23
|
||||
|
||||
// 172.[17-31].42.1/16
|
||||
mask := []byte{255, 255, 0, 0}
|
||||
for i := 17; i < 32; i++ {
|
||||
bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{172, byte(i), 42, 1}, Mask: mask})
|
||||
}
|
||||
// 10.[0-255].42.1/16
|
||||
for i := 0; i < 256; i++ {
|
||||
bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{10, byte(i), 42, 1}, Mask: mask})
|
||||
}
|
||||
// 192.168.[42-44].1/24
|
||||
mask24 := []byte{255, 255, 255, 0}
|
||||
for i := 42; i < 45; i++ {
|
||||
bridgeNetworks = append(bridgeNetworks, &net.IPNet{IP: []byte{192, 168, byte(i), 1}, Mask: mask24})
|
||||
}
|
||||
}
|
||||
|
||||
func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
||||
addrv4, _, err := i.addresses()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to retrieve bridge interface addresses: %v", err)
|
||||
}
|
||||
|
||||
// Check if we have an IP address already on the bridge.
|
||||
if !types.CompareIPNet(addrv4.IPNet, config.AddressIPv4) {
|
||||
if addrv4.IPNet != nil {
|
||||
// Make sure to store bridge network and default gateway before getting out.
|
||||
i.bridgeIPv4 = addrv4.IPNet
|
||||
i.gatewayIPv4 = addrv4.IPNet.IP
|
||||
return nil
|
||||
if err := netlink.AddrDel(i.Link, &addrv4); err != nil {
|
||||
return fmt.Errorf("failed to remove current ip address from bridge: %v", err)
|
||||
}
|
||||
|
||||
// Do not try to configure IPv4 on a non-default bridge unless you are
|
||||
// specifically asked to do so.
|
||||
if config.BridgeName != DefaultBridgeName && config.DefaultBridge {
|
||||
return NonDefaultBridgeNeedsIPError(config.BridgeName)
|
||||
}
|
||||
|
||||
bridgeIPv4, err := electBridgeIPv4(config)
|
||||
if err != nil {
|
||||
return err
|
||||
log.Debugf("Assigning address to bridge interface %s: %s", config.BridgeName, config.AddressIPv4)
|
||||
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: config.AddressIPv4}); err != nil {
|
||||
return &IPv4AddrAddError{IP: config.AddressIPv4, Err: err}
|
||||
}
|
||||
|
||||
log.Debugf("Creating bridge interface %s with network %s", config.BridgeName, bridgeIPv4)
|
||||
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil {
|
||||
return &IPv4AddrAddError{IP: bridgeIPv4, Err: err}
|
||||
}
|
||||
|
||||
// Store bridge network and default gateway
|
||||
i.bridgeIPv4 = bridgeIPv4
|
||||
i.gatewayIPv4 = i.bridgeIPv4.IP
|
||||
i.bridgeIPv4 = config.AddressIPv4
|
||||
i.gatewayIPv4 = config.AddressIPv4.IP
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func allocateBridgeIP(config *networkConfiguration, i *bridgeInterface) error {
|
||||
// Because of the way ipallocator manages the container address space,
|
||||
// reserve bridge address only if it belongs to the container network
|
||||
// (if defined), no need otherwise
|
||||
if config.FixedCIDR == nil || config.FixedCIDR.Contains(i.bridgeIPv4.IP) {
|
||||
if _, err := ipAllocator.RequestIP(i.bridgeIPv4, i.bridgeIPv4.IP); err != nil {
|
||||
return fmt.Errorf("failed to reserve bridge IP %s: %v", i.bridgeIPv4.IP.String(), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func electBridgeIPv4(config *networkConfiguration) (*net.IPNet, error) {
|
||||
// Use the requested IPv4 CIDR when available.
|
||||
if config.AddressIPv4 != nil {
|
||||
return config.AddressIPv4, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
nameservers := []string{}
|
||||
if resolvConf, _ := readResolvConf(); resolvConf != nil {
|
||||
nameservers = append(nameservers, getNameserversAsCIDR(resolvConf)...)
|
||||
}
|
||||
|
||||
// Try to automatically elect appropriate bridge IPv4 settings.
|
||||
for _, n := range bridgeNetworks {
|
||||
if err := netutils.CheckNameserverOverlaps(nameservers, n); err == nil {
|
||||
if err := netutils.CheckRouteOverlaps(n); err == nil {
|
||||
return n, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, IPv4AddrRangeError(config.BridgeName)
|
||||
}
|
||||
|
||||
func setupGatewayIPv4(config *networkConfiguration, i *bridgeInterface) error {
|
||||
if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) {
|
||||
return &ErrInvalidGateway{}
|
||||
}
|
||||
|
||||
// Because of the way ipallocator manages the container address space,
|
||||
// reserve default gw address only if it belongs to the container network
|
||||
// (if defined), no need otherwise
|
||||
if config.FixedCIDR == nil || config.FixedCIDR.Contains(config.DefaultGatewayIPv4) {
|
||||
if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil {
|
||||
return fmt.Errorf("failed to reserve default gateway %s: %v", config.DefaultGatewayIPv4.String(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Store requested default gateway
|
||||
i.gatewayIPv4 = config.DefaultGatewayIPv4
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
@ -57,19 +58,31 @@ func setupBridgeIPv6(config *networkConfiguration, i *bridgeInterface) error {
|
|||
i.bridgeIPv6 = bridgeIPv6
|
||||
i.gatewayIPv6 = i.bridgeIPv6.IP
|
||||
|
||||
if config.AddressIPv6 == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Setting route to global IPv6 subnet
|
||||
logrus.Debugf("Adding route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName)
|
||||
err = netlink.RouteAdd(&netlink.Route{
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
LinkIndex: i.Link.Attrs().Index,
|
||||
Dst: config.AddressIPv6,
|
||||
})
|
||||
if err != nil && !os.IsExist(err) {
|
||||
logrus.Errorf("Could not add route to IPv6 network %s via device %s", config.AddressIPv6.String(), config.BridgeName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupGatewayIPv6(config *networkConfiguration, i *bridgeInterface) error {
|
||||
if config.FixedCIDRv6 == nil {
|
||||
if config.AddressIPv6 == nil {
|
||||
return &ErrInvalidContainerSubnet{}
|
||||
}
|
||||
if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) {
|
||||
if !config.AddressIPv6.Contains(config.DefaultGatewayIPv6) {
|
||||
return &ErrInvalidGateway{}
|
||||
}
|
||||
if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store requested default gateway
|
||||
i.gatewayIPv6 = config.DefaultGatewayIPv6
|
||||
|
|
|
@ -23,7 +23,7 @@ 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{}) error {
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
|
@ -40,7 +40,7 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType)
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ 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{}) error {
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
|
@ -40,7 +40,7 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
return types.ForbiddenErrorf("network of type \"%s\" cannot be deleted", networkType)
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
|
@ -24,11 +26,27 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
|||
return fmt.Errorf("could not find endpoint with id %s", eid)
|
||||
}
|
||||
|
||||
if err := n.joinSandbox(); err != nil {
|
||||
return fmt.Errorf("network sandbox join failed: %v",
|
||||
err)
|
||||
s := n.getSubnetforIP(ep.addr)
|
||||
if s == nil {
|
||||
return fmt.Errorf("could not find subnet for endpoint %s", eid)
|
||||
}
|
||||
|
||||
if err := n.obtainVxlanID(s); err != nil {
|
||||
return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
|
||||
}
|
||||
|
||||
if err := n.joinSandbox(); err != nil {
|
||||
return fmt.Errorf("network sandbox join failed: %v", err)
|
||||
}
|
||||
|
||||
if err := n.joinSubnetSandbox(s); err != nil {
|
||||
return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
|
||||
}
|
||||
|
||||
// joinSubnetSandbox gets called when an endpoint comes up on a new subnet in the
|
||||
// overlay network. Hence the Endpoint count should be updated outside joinSubnetSandbox
|
||||
n.incEndpointCount()
|
||||
|
||||
sbox := n.sandbox()
|
||||
|
||||
name1, name2, err := createVethPair()
|
||||
|
@ -49,7 +67,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
|||
}
|
||||
|
||||
if err := sbox.AddInterface(name1, "veth",
|
||||
sbox.InterfaceOptions().Master("bridge1")); err != nil {
|
||||
sbox.InterfaceOptions().Master(s.brName)); err != nil {
|
||||
return fmt.Errorf("could not add veth pair inside the network sandbox: %v", err)
|
||||
}
|
||||
|
||||
|
@ -63,7 +81,16 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
|||
}
|
||||
|
||||
if err := netlink.LinkSetHardwareAddr(veth, ep.mac); err != nil {
|
||||
return fmt.Errorf("could not set mac address to the container interface: %v", err)
|
||||
return fmt.Errorf("could not set mac address (%v) to the container interface: %v", ep.mac, err)
|
||||
}
|
||||
|
||||
for _, sub := range n.subnets {
|
||||
if sub == s {
|
||||
continue
|
||||
}
|
||||
if err := jinfo.AddStaticRoute(sub.subnetIP, types.NEXTHOP, s.gwIP.IP); err != nil {
|
||||
log.Errorf("Adding subnet %s static route in network %q failed\n", s.subnetIP, n.id)
|
||||
}
|
||||
}
|
||||
|
||||
if iNames := jinfo.InterfaceName(); iNames != nil {
|
||||
|
@ -73,7 +100,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
|||
}
|
||||
}
|
||||
|
||||
d.peerDbAdd(nid, eid, ep.addr.IP, ep.mac,
|
||||
d.peerDbAdd(nid, eid, ep.addr.IP, ep.addr.Mask, ep.mac,
|
||||
net.ParseIP(d.bindAddress), true)
|
||||
d.pushLocalEndpointEvent("join", nid, eid)
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package overlay
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
|
@ -36,9 +35,18 @@ func (n *network) deleteEndpoint(eid string) {
|
|||
n.Unlock()
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
|
||||
epOptions map[string]interface{}) error {
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
var err error
|
||||
|
||||
if err = validateID(nid, eid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Since we perform lazy configuration make sure we try
|
||||
// configuring the driver when we enter CreateEndpoint since
|
||||
// CreateNetwork may not be called in every node.
|
||||
if err := d.configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -49,34 +57,22 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
|
|||
|
||||
ep := &endpoint{
|
||||
id: eid,
|
||||
addr: ifInfo.Address(),
|
||||
mac: ifInfo.MacAddress(),
|
||||
}
|
||||
if ep.addr == nil {
|
||||
return fmt.Errorf("create endpoint was not passed interface IP address")
|
||||
}
|
||||
|
||||
if epInfo != nil && epInfo.Interface() != nil {
|
||||
addr := epInfo.Interface().Address()
|
||||
ep.addr = &addr
|
||||
ep.mac = epInfo.Interface().MacAddress()
|
||||
n.addEndpoint(ep)
|
||||
return nil
|
||||
if s := n.getSubnetforIP(ep.addr); s == nil {
|
||||
return fmt.Errorf("no matching subnet for IP %q in network %q\n", ep.addr, nid)
|
||||
}
|
||||
|
||||
ipID, err := d.ipAllocator.GetID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not allocate ip from subnet %s: %v",
|
||||
bridgeSubnet.String(), err)
|
||||
}
|
||||
|
||||
ep.addr = &net.IPNet{
|
||||
Mask: bridgeSubnet.Mask,
|
||||
}
|
||||
ep.addr.IP = make([]byte, 4)
|
||||
|
||||
binary.BigEndian.PutUint32(ep.addr.IP, bridgeSubnetInt+ipID)
|
||||
|
||||
if ep.mac == nil {
|
||||
ep.mac = netutils.GenerateMACFromIP(ep.addr.IP)
|
||||
|
||||
err = epInfo.AddInterface(ep.mac, *ep.addr, net.IPNet{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not add interface to endpoint info: %v", err)
|
||||
if err := ifInfo.SetMacAddress(ep.mac); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
n.addEndpoint(ep)
|
||||
|
@ -99,7 +95,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
|
|||
return fmt.Errorf("endpoint id %q not found", eid)
|
||||
}
|
||||
|
||||
d.ipAllocator.Release(binary.BigEndian.Uint32(ep.addr.IP) - bridgeSubnetInt)
|
||||
n.deleteEndpoint(eid)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -9,37 +9,54 @@ import (
|
|||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipallocator"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
type networkTable map[string]*network
|
||||
|
||||
type subnet struct {
|
||||
once *sync.Once
|
||||
vxlanName string
|
||||
brName string
|
||||
vni uint32
|
||||
initErr error
|
||||
subnetIP *net.IPNet
|
||||
gwIP *net.IPNet
|
||||
}
|
||||
|
||||
type subnetJSON struct {
|
||||
SubnetIP string
|
||||
GwIP string
|
||||
Vni uint32
|
||||
}
|
||||
|
||||
type network struct {
|
||||
id string
|
||||
vni uint32
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
sbox osl.Sandbox
|
||||
endpoints endpointTable
|
||||
ipAllocator *ipallocator.IPAllocator
|
||||
gw net.IP
|
||||
vxlanName string
|
||||
driver *driver
|
||||
joinCnt int
|
||||
once *sync.Once
|
||||
initEpoch int
|
||||
initErr error
|
||||
subnets []*subnet
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("invalid network id")
|
||||
}
|
||||
|
||||
// Since we perform lazy configuration make sure we try
|
||||
// configuring the driver when we enter CreateNetwork
|
||||
if err := d.configure(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -49,19 +66,43 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}) error {
|
|||
driver: d,
|
||||
endpoints: endpointTable{},
|
||||
once: &sync.Once{},
|
||||
subnets: []*subnet{},
|
||||
}
|
||||
|
||||
n.gw = bridgeIP.IP
|
||||
for _, ipd := range ipV4Data {
|
||||
s := &subnet{
|
||||
subnetIP: ipd.Pool,
|
||||
gwIP: ipd.Gateway,
|
||||
once: &sync.Once{},
|
||||
}
|
||||
n.subnets = append(n.subnets, s)
|
||||
}
|
||||
|
||||
if err := n.writeToStore(); err != nil {
|
||||
return fmt.Errorf("failed to update data store for network %v: %v", n.id, err)
|
||||
}
|
||||
|
||||
d.addNetwork(n)
|
||||
|
||||
if err := n.obtainVxlanID(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/* func (d *driver) createNetworkfromStore(nid string) (*network, error) {
|
||||
n := &network{
|
||||
id: nid,
|
||||
driver: d,
|
||||
endpoints: endpointTable{},
|
||||
once: &sync.Once{},
|
||||
subnets: []*subnet{},
|
||||
}
|
||||
|
||||
err := d.store.GetObject(datastore.Key(n.Key()...), n)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get network %q from data store, %v", nid, err)
|
||||
}
|
||||
return n, nil
|
||||
}*/
|
||||
|
||||
func (d *driver) DeleteNetwork(nid string) error {
|
||||
if nid == "" {
|
||||
return fmt.Errorf("invalid network id")
|
||||
|
@ -77,15 +118,13 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
return n.releaseVxlanID()
|
||||
}
|
||||
|
||||
func (n *network) joinSandbox() error {
|
||||
func (n *network) incEndpointCount() {
|
||||
n.Lock()
|
||||
if n.joinCnt != 0 {
|
||||
defer n.Unlock()
|
||||
n.joinCnt++
|
||||
n.Unlock()
|
||||
return nil
|
||||
}
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (n *network) joinSandbox() error {
|
||||
// If there is a race between two go routines here only one will win
|
||||
// the other will wait.
|
||||
n.once.Do(func() {
|
||||
|
@ -94,16 +133,14 @@ func (n *network) joinSandbox() error {
|
|||
n.initErr = n.initSandbox()
|
||||
})
|
||||
|
||||
// Increment joinCnt in all the goroutines only when the one time initSandbox
|
||||
// was a success.
|
||||
n.Lock()
|
||||
if n.initErr == nil {
|
||||
n.joinCnt++
|
||||
}
|
||||
err := n.initErr
|
||||
n.Unlock()
|
||||
return n.initErr
|
||||
}
|
||||
|
||||
return err
|
||||
func (n *network) joinSubnetSandbox(s *subnet) error {
|
||||
s.once.Do(func() {
|
||||
s.initErr = n.initSubnetSandbox(s)
|
||||
})
|
||||
return s.initErr
|
||||
}
|
||||
|
||||
func (n *network) leaveSandbox() {
|
||||
|
@ -118,6 +155,9 @@ func (n *network) leaveSandbox() {
|
|||
// Reinitialize the once variable so that we will be able to trigger one time
|
||||
// sandbox initialization(again) when another container joins subsequently.
|
||||
n.once = &sync.Once{}
|
||||
for _, s := range n.subnets {
|
||||
s.once = &sync.Once{}
|
||||
}
|
||||
n.Unlock()
|
||||
|
||||
n.destroySandbox()
|
||||
|
@ -130,14 +170,50 @@ func (n *network) destroySandbox() {
|
|||
iface.Remove()
|
||||
}
|
||||
|
||||
if err := deleteVxlan(n.vxlanName); err != nil {
|
||||
for _, s := range n.subnets {
|
||||
if s.vxlanName != "" {
|
||||
err := deleteVxlan(s.vxlanName)
|
||||
if err != nil {
|
||||
logrus.Warnf("could not cleanup sandbox properly: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
sbox.Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
func (n *network) initSubnetSandbox(s *subnet) error {
|
||||
// create a bridge and vxlan device for this subnet and move it to the sandbox
|
||||
brName, err := netutils.GenerateIfaceName("bridge", 7)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sbox := n.sandbox()
|
||||
|
||||
if err := sbox.AddInterface(brName, "br",
|
||||
sbox.InterfaceOptions().Address(s.gwIP),
|
||||
sbox.InterfaceOptions().Bridge(true)); err != nil {
|
||||
return fmt.Errorf("bridge creation in sandbox failed for subnet %q: %v", s.subnetIP.IP.String(), err)
|
||||
}
|
||||
|
||||
vxlanName, err := createVxlan(n.vxlanID(s))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sbox.AddInterface(vxlanName, "vxlan",
|
||||
sbox.InterfaceOptions().Master(brName)); err != nil {
|
||||
return fmt.Errorf("vxlan interface creation failed for subnet %q: %v", s.subnetIP.IP.String(), err)
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
s.vxlanName = vxlanName
|
||||
s.brName = brName
|
||||
n.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) initSandbox() error {
|
||||
n.Lock()
|
||||
n.initEpoch++
|
||||
|
@ -149,15 +225,10 @@ func (n *network) initSandbox() error {
|
|||
return fmt.Errorf("could not create network sandbox: %v", err)
|
||||
}
|
||||
|
||||
// Add a bridge inside the namespace
|
||||
if err := sbox.AddInterface("bridge1", "br",
|
||||
sbox.InterfaceOptions().Address(bridgeIP),
|
||||
sbox.InterfaceOptions().Bridge(true)); err != nil {
|
||||
return fmt.Errorf("could not create bridge inside the network sandbox: %v", err)
|
||||
}
|
||||
|
||||
n.setSandbox(sbox)
|
||||
|
||||
n.driver.peerDbUpdateSandbox(n.id)
|
||||
|
||||
var nlSock *nl.NetlinkSocket
|
||||
sbox.InvokeFunc(func() {
|
||||
nlSock, err = nl.Subscribe(syscall.NETLINK_ROUTE, syscall.RTNLGRP_NEIGH)
|
||||
|
@ -167,27 +238,6 @@ func (n *network) initSandbox() error {
|
|||
})
|
||||
|
||||
go n.watchMiss(nlSock)
|
||||
return n.initVxlan()
|
||||
}
|
||||
|
||||
func (n *network) initVxlan() error {
|
||||
var vxlanName string
|
||||
n.Lock()
|
||||
sbox := n.sbox
|
||||
n.Unlock()
|
||||
|
||||
vxlanName, err := createVxlan(n.vxlanID())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = sbox.AddInterface(vxlanName, "vxlan",
|
||||
sbox.InterfaceOptions().Master("bridge1")); err != nil {
|
||||
return fmt.Errorf("could not add vxlan interface inside the network sandbox: %v", err)
|
||||
}
|
||||
|
||||
n.vxlanName = vxlanName
|
||||
n.driver.peerDbUpdateSandbox(n.id)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -218,14 +268,14 @@ func (n *network) watchMiss(nlSock *nl.NetlinkSocket) {
|
|||
continue
|
||||
}
|
||||
|
||||
mac, vtep, err := n.driver.resolvePeer(n.id, neigh.IP)
|
||||
mac, IPmask, vtep, err := n.driver.resolvePeer(n.id, neigh.IP)
|
||||
if err != nil {
|
||||
logrus.Errorf("could not resolve peer %q: %v", neigh.IP, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := n.driver.peerAdd(n.id, "dummy", neigh.IP, mac, vtep, true); err != nil {
|
||||
logrus.Errorf("could not add neighbor entry for missed peer: %v", err)
|
||||
if err := n.driver.peerAdd(n.id, "dummy", neigh.IP, IPmask, mac, vtep, true); err != nil {
|
||||
logrus.Errorf("could not add neighbor entry for missed peer %q: %v", neigh.IP, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,9 +295,34 @@ func (d *driver) deleteNetwork(nid string) {
|
|||
|
||||
func (d *driver) network(nid string) *network {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
networks := d.networks
|
||||
d.Unlock()
|
||||
|
||||
return d.networks[nid]
|
||||
n, ok := networks[nid]
|
||||
if !ok {
|
||||
n = d.getNetworkFromStore(nid)
|
||||
if n != nil {
|
||||
n.driver = d
|
||||
n.endpoints = endpointTable{}
|
||||
n.once = &sync.Once{}
|
||||
networks[nid] = n
|
||||
}
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (d *driver) getNetworkFromStore(nid string) *network {
|
||||
if d.store == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
n := &network{id: nid}
|
||||
if err := d.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *network) sandbox() osl.Sandbox {
|
||||
|
@ -263,16 +338,16 @@ func (n *network) setSandbox(sbox osl.Sandbox) {
|
|||
n.Unlock()
|
||||
}
|
||||
|
||||
func (n *network) vxlanID() uint32 {
|
||||
func (n *network) vxlanID(s *subnet) uint32 {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
return n.vni
|
||||
return s.vni
|
||||
}
|
||||
|
||||
func (n *network) setVxlanID(vni uint32) {
|
||||
func (n *network) setVxlanID(s *subnet, vni uint32) {
|
||||
n.Lock()
|
||||
n.vni = vni
|
||||
s.vni = vni
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
|
@ -285,11 +360,22 @@ func (n *network) KeyPrefix() []string {
|
|||
}
|
||||
|
||||
func (n *network) Value() []byte {
|
||||
b, err := json.Marshal(n.vxlanID())
|
||||
netJSON := []*subnetJSON{}
|
||||
|
||||
for _, s := range n.subnets {
|
||||
sj := &subnetJSON{
|
||||
SubnetIP: s.subnetIP.String(),
|
||||
GwIP: s.gwIP.String(),
|
||||
Vni: s.vni,
|
||||
}
|
||||
netJSON = append(netJSON, sj)
|
||||
}
|
||||
|
||||
b, err := json.Marshal(netJSON)
|
||||
|
||||
if err != nil {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
|
@ -311,15 +397,45 @@ func (n *network) Skip() bool {
|
|||
}
|
||||
|
||||
func (n *network) SetValue(value []byte) error {
|
||||
var vni uint32
|
||||
err := json.Unmarshal(value, &vni)
|
||||
if err == nil {
|
||||
n.setVxlanID(vni)
|
||||
}
|
||||
var newNet bool
|
||||
netJSON := []*subnetJSON{}
|
||||
|
||||
err := json.Unmarshal(value, &netJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(n.subnets) == 0 {
|
||||
newNet = true
|
||||
}
|
||||
|
||||
for _, sj := range netJSON {
|
||||
subnetIPstr := sj.SubnetIP
|
||||
gwIPstr := sj.GwIP
|
||||
vni := sj.Vni
|
||||
|
||||
subnetIP, _ := types.ParseCIDR(subnetIPstr)
|
||||
gwIP, _ := types.ParseCIDR(gwIPstr)
|
||||
|
||||
if newNet {
|
||||
s := &subnet{
|
||||
subnetIP: subnetIP,
|
||||
gwIP: gwIP,
|
||||
vni: vni,
|
||||
once: &sync.Once{},
|
||||
}
|
||||
n.subnets = append(n.subnets, s)
|
||||
} else {
|
||||
sNet := n.getMatchingSubnet(subnetIP)
|
||||
if sNet != nil {
|
||||
sNet.vni = vni
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) DataScope() datastore.DataScope {
|
||||
func (n *network) DataScope() string {
|
||||
return datastore.GlobalScope
|
||||
}
|
||||
|
||||
|
@ -332,7 +448,7 @@ func (n *network) releaseVxlanID() error {
|
|||
return fmt.Errorf("no datastore configured. cannot release vxlan id")
|
||||
}
|
||||
|
||||
if n.vxlanID() == 0 {
|
||||
if len(n.subnets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -346,38 +462,80 @@ func (n *network) releaseVxlanID() error {
|
|||
return fmt.Errorf("failed to delete network to vxlan id map: %v", err)
|
||||
}
|
||||
|
||||
n.driver.vxlanIdm.Release(n.vxlanID())
|
||||
n.setVxlanID(0)
|
||||
for _, s := range n.subnets {
|
||||
n.driver.vxlanIdm.Release(uint64(n.vxlanID(s)))
|
||||
n.setVxlanID(s, 0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *network) obtainVxlanID() error {
|
||||
func (n *network) obtainVxlanID(s *subnet) error {
|
||||
//return if the subnet already has a vxlan id assigned
|
||||
if s.vni != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if n.driver.store == nil {
|
||||
return fmt.Errorf("no datastore configured. cannot obtain vxlan id")
|
||||
}
|
||||
|
||||
for {
|
||||
var vxlanID uint32
|
||||
if err := n.driver.store.GetObject(datastore.Key(n.Key()...), n); err != nil {
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
vxlanID, err = n.driver.vxlanIdm.GetID()
|
||||
return fmt.Errorf("getting network %q from datastore failed %v", n.id, err)
|
||||
}
|
||||
|
||||
if s.vni == 0 {
|
||||
vxlanID, err := n.driver.vxlanIdm.GetID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to allocate vxlan id: %v", err)
|
||||
}
|
||||
|
||||
n.setVxlanID(vxlanID)
|
||||
n.setVxlanID(s, uint32(vxlanID))
|
||||
if err := n.writeToStore(); err != nil {
|
||||
n.driver.vxlanIdm.Release(n.vxlanID())
|
||||
n.setVxlanID(0)
|
||||
n.driver.vxlanIdm.Release(uint64(n.vxlanID(s)))
|
||||
n.setVxlanID(s, 0)
|
||||
if err == datastore.ErrKeyModified {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("failed to update data store with vxlan id: %v", err)
|
||||
return fmt.Errorf("network %q failed to update data store: %v", n.id, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to obtain vxlan id from data store: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getSubnetforIP returns the subnet to which the given IP belongs
|
||||
func (n *network) getSubnetforIP(ip *net.IPNet) *subnet {
|
||||
for _, s := range n.subnets {
|
||||
// first check if the mask lengths are the same
|
||||
i, _ := s.subnetIP.Mask.Size()
|
||||
j, _ := ip.Mask.Size()
|
||||
if i != j {
|
||||
continue
|
||||
}
|
||||
if s.subnetIP.Contains(ip.IP) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getMatchingSubnet return the network's subnet that matches the input
|
||||
func (n *network) getMatchingSubnet(ip *net.IPNet) *subnet {
|
||||
if ip == nil {
|
||||
return nil
|
||||
}
|
||||
for _, s := range n.subnets {
|
||||
// first check if the mask lengths are the same
|
||||
i, _ := s.subnetIP.Mask.Size()
|
||||
j, _ := ip.Mask.Size()
|
||||
if i != j {
|
||||
continue
|
||||
}
|
||||
if s.subnetIP.IP.Equal(ip.IP) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func (d *driver) serfInit() error {
|
|||
config.UserCoalescePeriod = 1 * time.Second
|
||||
config.UserQuiescentPeriod = 50 * time.Millisecond
|
||||
|
||||
config.LogOutput = logrus.StandardLogger().Out
|
||||
config.LogOutput = &logWriter{}
|
||||
|
||||
s, err := serf.Create(config)
|
||||
if err != nil {
|
||||
|
@ -83,34 +83,35 @@ func (d *driver) notifyEvent(event ovNotify) {
|
|||
n := d.network(event.nid)
|
||||
ep := n.endpoint(event.eid)
|
||||
|
||||
ePayload := fmt.Sprintf("%s %s %s", event.action, ep.addr.IP.String(), ep.mac.String())
|
||||
ePayload := fmt.Sprintf("%s %s %s %s", event.action, ep.addr.IP.String(),
|
||||
net.IP(ep.addr.Mask).String(), ep.mac.String())
|
||||
eName := fmt.Sprintf("jl %s %s %s", d.serfInstance.LocalMember().Addr.String(),
|
||||
event.nid, event.eid)
|
||||
|
||||
if err := d.serfInstance.UserEvent(eName, []byte(ePayload), true); err != nil {
|
||||
fmt.Printf("Sending user event failed: %v\n", err)
|
||||
logrus.Errorf("Sending user event failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) processEvent(u serf.UserEvent) {
|
||||
fmt.Printf("Received user event name:%s, payload:%s\n", u.Name,
|
||||
logrus.Debugf("Received user event name:%s, payload:%s\n", u.Name,
|
||||
string(u.Payload))
|
||||
|
||||
var dummy, action, vtepStr, nid, eid, ipStr, macStr string
|
||||
var dummy, action, vtepStr, nid, eid, ipStr, maskStr, macStr string
|
||||
if _, err := fmt.Sscan(u.Name, &dummy, &vtepStr, &nid, &eid); err != nil {
|
||||
fmt.Printf("Failed to scan name string: %v\n", err)
|
||||
}
|
||||
|
||||
if _, err := fmt.Sscan(string(u.Payload), &action,
|
||||
&ipStr, &macStr); err != nil {
|
||||
&ipStr, &maskStr, &macStr); err != nil {
|
||||
fmt.Printf("Failed to scan value string: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Parsed data = %s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, macStr)
|
||||
logrus.Debugf("Parsed data = %s/%s/%s/%s/%s/%s\n", nid, eid, vtepStr, ipStr, maskStr, macStr)
|
||||
|
||||
mac, err := net.ParseMAC(macStr)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to parse mac: %v\n", err)
|
||||
logrus.Errorf("Failed to parse mac: %v\n", err)
|
||||
}
|
||||
|
||||
if d.serfInstance.LocalMember().Addr.String() == vtepStr {
|
||||
|
@ -119,20 +120,20 @@ func (d *driver) processEvent(u serf.UserEvent) {
|
|||
|
||||
switch action {
|
||||
case "join":
|
||||
if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), mac,
|
||||
if err := d.peerAdd(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
|
||||
net.ParseIP(vtepStr), true); err != nil {
|
||||
fmt.Printf("Peer add failed in the driver: %v\n", err)
|
||||
logrus.Errorf("Peer add failed in the driver: %v\n", err)
|
||||
}
|
||||
case "leave":
|
||||
if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), mac,
|
||||
if err := d.peerDelete(nid, eid, net.ParseIP(ipStr), net.IPMask(net.ParseIP(maskStr).To4()), mac,
|
||||
net.ParseIP(vtepStr), true); err != nil {
|
||||
fmt.Printf("Peer delete failed in the driver: %v\n", err)
|
||||
logrus.Errorf("Peer delete failed in the driver: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) processQuery(q *serf.Query) {
|
||||
fmt.Printf("Received query name:%s, payload:%s\n", q.Name,
|
||||
logrus.Debugf("Received query name:%s, payload:%s\n", q.Name,
|
||||
string(q.Payload))
|
||||
|
||||
var nid, ipStr string
|
||||
|
@ -140,38 +141,38 @@ func (d *driver) processQuery(q *serf.Query) {
|
|||
fmt.Printf("Failed to scan query payload string: %v\n", err)
|
||||
}
|
||||
|
||||
peerMac, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr))
|
||||
peerMac, peerIPMask, vtep, err := d.peerDbSearch(nid, net.ParseIP(ipStr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
q.Respond([]byte(fmt.Sprintf("%s %s", peerMac.String(), vtep.String())))
|
||||
q.Respond([]byte(fmt.Sprintf("%s %s %s", peerMac.String(), net.IP(peerIPMask).String(), vtep.String())))
|
||||
}
|
||||
|
||||
func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IP, error) {
|
||||
func (d *driver) resolvePeer(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
|
||||
qPayload := fmt.Sprintf("%s %s", string(nid), peerIP.String())
|
||||
resp, err := d.serfInstance.Query("peerlookup", []byte(qPayload), nil)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err)
|
||||
return nil, nil, nil, fmt.Errorf("resolving peer by querying the cluster failed: %v", err)
|
||||
}
|
||||
|
||||
respCh := resp.ResponseCh()
|
||||
select {
|
||||
case r := <-respCh:
|
||||
var macStr, vtepStr string
|
||||
if _, err := fmt.Sscan(string(r.Payload), &macStr, &vtepStr); err != nil {
|
||||
return nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err)
|
||||
var macStr, maskStr, vtepStr string
|
||||
if _, err := fmt.Sscan(string(r.Payload), &macStr, &maskStr, &vtepStr); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("bad response %q for the resolve query: %v", string(r.Payload), err)
|
||||
}
|
||||
|
||||
mac, err := net.ParseMAC(macStr)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse mac: %v", err)
|
||||
return nil, nil, nil, fmt.Errorf("failed to parse mac: %v", err)
|
||||
}
|
||||
|
||||
return mac, net.ParseIP(vtepStr), nil
|
||||
return mac, net.IPMask(net.ParseIP(maskStr).To4()), net.ParseIP(vtepStr), nil
|
||||
|
||||
case <-time.After(time.Second):
|
||||
return nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster")
|
||||
return nil, nil, nil, fmt.Errorf("timed out resolving peer by querying the cluster")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,7 +193,7 @@ func (d *driver) startSerfLoop(eventCh chan serf.Event, notifyCh chan ovNotify,
|
|||
}
|
||||
|
||||
if err := d.serfInstance.Leave(); err != nil {
|
||||
fmt.Printf("failed leaving the cluster: %v\n", err)
|
||||
logrus.Errorf("failed leaving the cluster: %v\n", err)
|
||||
}
|
||||
|
||||
d.serfInstance.Shutdown()
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
package overlay
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/idm"
|
||||
|
@ -44,36 +41,8 @@ type driver struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
var (
|
||||
bridgeSubnet, bridgeIP *net.IPNet
|
||||
once sync.Once
|
||||
bridgeSubnetInt uint32
|
||||
)
|
||||
|
||||
func onceInit() {
|
||||
var err error
|
||||
_, bridgeSubnet, err = net.ParseCIDR("172.21.0.0/16")
|
||||
if err != nil {
|
||||
panic("could not parse cid 172.21.0.0/16")
|
||||
}
|
||||
|
||||
bridgeSubnetInt = binary.BigEndian.Uint32(bridgeSubnet.IP.To4())
|
||||
|
||||
ip, subnet, err := net.ParseCIDR("172.21.255.254/16")
|
||||
if err != nil {
|
||||
panic("could not parse cid 172.21.255.254/16")
|
||||
}
|
||||
|
||||
bridgeIP = &net.IPNet{
|
||||
IP: ip,
|
||||
Mask: subnet.Mask,
|
||||
}
|
||||
}
|
||||
|
||||
// Init registers a new instance of overlay driver
|
||||
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
||||
once.Do(onceInit)
|
||||
|
||||
c := driverapi.Capability{
|
||||
DataScope: datastore.GlobalScope,
|
||||
}
|
||||
|
@ -110,21 +79,21 @@ func (d *driver) configure() error {
|
|||
}
|
||||
|
||||
d.once.Do(func() {
|
||||
provider, provOk := d.config[netlabel.KVProvider]
|
||||
provURL, urlOk := d.config[netlabel.KVProviderURL]
|
||||
provider, provOk := d.config[netlabel.GlobalKVProvider]
|
||||
provURL, urlOk := d.config[netlabel.GlobalKVProviderURL]
|
||||
|
||||
if provOk && urlOk {
|
||||
cfg := &config.DatastoreCfg{
|
||||
Client: config.DatastoreClientCfg{
|
||||
cfg := &datastore.ScopeCfg{
|
||||
Client: datastore.ScopeClientCfg{
|
||||
Provider: provider.(string),
|
||||
Address: provURL.(string),
|
||||
},
|
||||
}
|
||||
provConfig, confOk := d.config[netlabel.KVProviderConfig]
|
||||
provConfig, confOk := d.config[netlabel.GlobalKVProviderConfig]
|
||||
if confOk {
|
||||
cfg.Client.Config = provConfig.(*store.Config)
|
||||
}
|
||||
d.store, err = datastore.NewDataStore(cfg)
|
||||
d.store, err = datastore.NewDataStore(datastore.GlobalScope, cfg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to initialize data store: %v", err)
|
||||
return
|
||||
|
|
|
@ -15,6 +15,7 @@ type peerKey struct {
|
|||
type peerEntry struct {
|
||||
eid string
|
||||
vtep net.IP
|
||||
peerIPMask net.IPMask
|
||||
inSandbox bool
|
||||
isLocal bool
|
||||
}
|
||||
|
@ -98,16 +99,18 @@ func (d *driver) peerDbNetworkWalk(nid string, f func(*peerKey, *peerEntry) bool
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IP, error) {
|
||||
func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.IPMask, net.IP, error) {
|
||||
var (
|
||||
peerMac net.HardwareAddr
|
||||
vtep net.IP
|
||||
peerIPMask net.IPMask
|
||||
found bool
|
||||
)
|
||||
|
||||
err := d.peerDbNetworkWalk(nid, func(pKey *peerKey, pEntry *peerEntry) bool {
|
||||
if pKey.peerIP.Equal(peerIP) {
|
||||
peerMac = pKey.peerMac
|
||||
peerIPMask = pEntry.peerIPMask
|
||||
vtep = pEntry.vtep
|
||||
found = true
|
||||
return found
|
||||
|
@ -117,17 +120,17 @@ func (d *driver) peerDbSearch(nid string, peerIP net.IP) (net.HardwareAddr, net.
|
|||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
|
||||
return nil, nil, nil, fmt.Errorf("peerdb search for peer ip %q failed: %v", peerIP, err)
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
|
||||
return nil, nil, nil, fmt.Errorf("peer ip %q not found in peerdb", peerIP)
|
||||
}
|
||||
|
||||
return peerMac, vtep, nil
|
||||
return peerMac, peerIPMask, vtep, nil
|
||||
}
|
||||
|
||||
func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP,
|
||||
func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
|
||||
peerMac net.HardwareAddr, vtep net.IP, isLocal bool) {
|
||||
|
||||
peerDbWg.Wait()
|
||||
|
@ -151,6 +154,7 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP,
|
|||
pEntry := peerEntry{
|
||||
eid: eid,
|
||||
vtep: vtep,
|
||||
peerIPMask: peerIPMask,
|
||||
isLocal: isLocal,
|
||||
}
|
||||
|
||||
|
@ -159,7 +163,7 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP,
|
|||
pMap.Unlock()
|
||||
}
|
||||
|
||||
func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP,
|
||||
func (d *driver) peerDbDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
|
||||
peerMac net.HardwareAddr, vtep net.IP) {
|
||||
peerDbWg.Wait()
|
||||
|
||||
|
@ -209,7 +213,7 @@ func (d *driver) peerDbUpdateSandbox(nid string) {
|
|||
// a copy of pEntry before capturing it in the following closure.
|
||||
entry := pEntry
|
||||
op := func() {
|
||||
if err := d.peerAdd(nid, entry.eid, pKey.peerIP,
|
||||
if err := d.peerAdd(nid, entry.eid, pKey.peerIP, entry.peerIPMask,
|
||||
pKey.peerMac, entry.vtep,
|
||||
false); err != nil {
|
||||
fmt.Printf("peerdbupdate in sandbox failed for ip %s and mac %s: %v",
|
||||
|
@ -228,7 +232,7 @@ func (d *driver) peerDbUpdateSandbox(nid string) {
|
|||
peerDbWg.Done()
|
||||
}
|
||||
|
||||
func (d *driver) peerAdd(nid, eid string, peerIP net.IP,
|
||||
func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
|
||||
peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {
|
||||
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
|
@ -236,7 +240,7 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP,
|
|||
}
|
||||
|
||||
if updateDb {
|
||||
d.peerDbAdd(nid, eid, peerIP, peerMac, vtep, false)
|
||||
d.peerDbAdd(nid, eid, peerIP, peerIPMask, peerMac, vtep, false)
|
||||
}
|
||||
|
||||
n := d.network(nid)
|
||||
|
@ -249,13 +253,31 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP,
|
|||
return nil
|
||||
}
|
||||
|
||||
IP := &net.IPNet{
|
||||
IP: peerIP,
|
||||
Mask: peerIPMask,
|
||||
}
|
||||
|
||||
s := n.getSubnetforIP(IP)
|
||||
if s == nil {
|
||||
return fmt.Errorf("couldn't find the subnet %q in network %q\n", IP.String(), n.id)
|
||||
}
|
||||
|
||||
if err := n.obtainVxlanID(s); err != nil {
|
||||
return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
|
||||
}
|
||||
|
||||
if err := n.joinSubnetSandbox(s); err != nil {
|
||||
return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
|
||||
}
|
||||
|
||||
// Add neighbor entry for the peer IP
|
||||
if err := sbox.AddNeighbor(peerIP, peerMac, sbox.NeighborOptions().LinkName(n.vxlanName)); err != nil {
|
||||
if err := sbox.AddNeighbor(peerIP, peerMac, sbox.NeighborOptions().LinkName(s.vxlanName)); err != nil {
|
||||
return fmt.Errorf("could not add neigbor entry into the sandbox: %v", err)
|
||||
}
|
||||
|
||||
// Add fdb entry to the bridge for the peer mac
|
||||
if err := sbox.AddNeighbor(vtep, peerMac, sbox.NeighborOptions().LinkName(n.vxlanName),
|
||||
if err := sbox.AddNeighbor(vtep, peerMac, sbox.NeighborOptions().LinkName(s.vxlanName),
|
||||
sbox.NeighborOptions().Family(syscall.AF_BRIDGE)); err != nil {
|
||||
return fmt.Errorf("could not add fdb entry into the sandbox: %v", err)
|
||||
}
|
||||
|
@ -263,7 +285,7 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP,
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) peerDelete(nid, eid string, peerIP net.IP,
|
||||
func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
|
||||
peerMac net.HardwareAddr, vtep net.IP, updateDb bool) error {
|
||||
|
||||
if err := validateID(nid, eid); err != nil {
|
||||
|
@ -271,7 +293,7 @@ func (d *driver) peerDelete(nid, eid string, peerIP net.IP,
|
|||
}
|
||||
|
||||
if updateDb {
|
||||
d.peerDbDelete(nid, eid, peerIP, peerMac, vtep)
|
||||
d.peerDbDelete(nid, eid, peerIP, peerIPMask, peerMac, vtep)
|
||||
}
|
||||
|
||||
n := d.network(nid)
|
||||
|
|
|
@ -34,6 +34,9 @@ type CreateNetworkRequest struct {
|
|||
|
||||
// A free form map->object interface for communication of options.
|
||||
Options map[string]interface{}
|
||||
|
||||
// IPAMData contains the address pool information for this network
|
||||
IPv4Data, IPv6Data []driverapi.IPAMData
|
||||
}
|
||||
|
||||
// CreateNetworkResponse is the response to the CreateNetworkRequest.
|
||||
|
|
|
@ -82,10 +82,12 @@ func (d *driver) call(methodName string, arg interface{}, retVal maybeError) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) CreateNetwork(id string, options map[string]interface{}) error {
|
||||
func (d *driver) CreateNetwork(id string, options map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
create := &api.CreateNetworkRequest{
|
||||
NetworkID: id,
|
||||
Options: options,
|
||||
IPv4Data: ipV4Data,
|
||||
IPv6Data: ipV6Data,
|
||||
}
|
||||
return d.call("CreateNetwork", create, &api.CreateNetworkResponse{})
|
||||
}
|
||||
|
@ -95,23 +97,22 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
return d.call("DeleteNetwork", delete, &api.DeleteNetworkResponse{})
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
|
||||
var reqIface *api.EndpointInterface
|
||||
|
||||
if epInfo == nil {
|
||||
return fmt.Errorf("must not be called with nil EndpointInfo")
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
||||
if ifInfo == nil {
|
||||
return fmt.Errorf("must not be called with nil InterfaceInfo")
|
||||
}
|
||||
|
||||
iface := epInfo.Interface()
|
||||
if iface != nil {
|
||||
addr4 := iface.Address()
|
||||
addr6 := iface.AddressIPv6()
|
||||
reqIface = &api.EndpointInterface{
|
||||
Address: addr4.String(),
|
||||
AddressIPv6: addr6.String(),
|
||||
MacAddress: iface.MacAddress().String(),
|
||||
reqIface := &api.EndpointInterface{}
|
||||
if ifInfo.Address() != nil {
|
||||
reqIface.Address = ifInfo.Address().String()
|
||||
}
|
||||
if ifInfo.AddressIPv6() != nil {
|
||||
reqIface.AddressIPv6 = ifInfo.AddressIPv6().String()
|
||||
}
|
||||
if ifInfo.MacAddress() != nil {
|
||||
reqIface.MacAddress = ifInfo.MacAddress().String()
|
||||
}
|
||||
|
||||
create := &api.CreateEndpointRequest{
|
||||
NetworkID: nid,
|
||||
EndpointID: eid,
|
||||
|
@ -127,24 +128,27 @@ func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if reqIface != nil && inIface != nil {
|
||||
// We're not supposed to add interface if there is already
|
||||
// one. Attempt to roll back
|
||||
return errorWithRollback("driver attempted to add interface ignoring the one provided", d.DeleteEndpoint(nid, eid))
|
||||
if inIface == nil {
|
||||
// Remote driver did not set any field
|
||||
return nil
|
||||
}
|
||||
|
||||
if inIface != nil {
|
||||
var addr4, addr6 net.IPNet
|
||||
if inIface.MacAddress != nil {
|
||||
if err := ifInfo.SetMacAddress(inIface.MacAddress); err != nil {
|
||||
return errorWithRollback(fmt.Sprintf("driver modified interface MAC address: %v", err), d.DeleteEndpoint(nid, eid))
|
||||
}
|
||||
}
|
||||
if inIface.Address != nil {
|
||||
addr4 = *(inIface.Address)
|
||||
if err := ifInfo.SetIPAddress(inIface.Address); err != nil {
|
||||
return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid))
|
||||
}
|
||||
}
|
||||
if inIface.AddressIPv6 != nil {
|
||||
addr6 = *(inIface.AddressIPv6)
|
||||
}
|
||||
if err := epInfo.AddInterface(inIface.MacAddress, addr4, addr6); err != nil {
|
||||
return errorWithRollback(fmt.Sprintf("failed to AddInterface %v: %s", inIface, err), d.DeleteEndpoint(nid, eid))
|
||||
if err := ifInfo.SetIPAddress(inIface.AddressIPv6); err != nil {
|
||||
return errorWithRollback(fmt.Sprintf("driver modified interface address: %v", err), d.DeleteEndpoint(nid, eid))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -193,11 +197,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
|
|||
}
|
||||
|
||||
ifaceName := res.InterfaceName
|
||||
if jinfo.InterfaceName() != nil && ifaceName == nil {
|
||||
return fmt.Errorf("no interface name information received while one is expected")
|
||||
}
|
||||
|
||||
if iface := jinfo.InterfaceName(); iface != nil {
|
||||
if iface := jinfo.InterfaceName(); iface != nil && ifaceName != nil {
|
||||
if err := iface.SetNames(ifaceName.SrcName, ifaceName.DstPrefix); err != nil {
|
||||
return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid))
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ func parseStaticRoutes(r api.JoinResponse) ([]*types.StaticRoute, error) {
|
|||
outRoute := &types.StaticRoute{RouteType: inRoute.RouteType}
|
||||
|
||||
if inRoute.Destination != "" {
|
||||
if outRoute.Destination, err = toAddr(inRoute.Destination); err != nil {
|
||||
if outRoute.Destination, err = types.ParseCIDR(inRoute.Destination); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -304,12 +304,12 @@ func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) {
|
|||
var err error
|
||||
outIf = &api.Interface{}
|
||||
if inIf.Address != "" {
|
||||
if outIf.Address, err = toAddr(inIf.Address); err != nil {
|
||||
if outIf.Address, err = types.ParseCIDR(inIf.Address); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if inIf.AddressIPv6 != "" {
|
||||
if outIf.AddressIPv6, err = toAddr(inIf.AddressIPv6); err != nil {
|
||||
if outIf.AddressIPv6, err = types.ParseCIDR(inIf.AddressIPv6); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -322,12 +322,3 @@ func parseInterface(r api.CreateEndpointResponse) (*api.Interface, error) {
|
|||
|
||||
return outIf, nil
|
||||
}
|
||||
|
||||
func toAddr(ipAddr string) (*net.IPNet, error) {
|
||||
ip, ipnet, err := net.ParseCIDR(ipAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ipnet.IP = ip
|
||||
return ipnet, nil
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ 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{}) error {
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ func (d *driver) DeleteNetwork(nid string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
289
vendor/src/github.com/docker/libnetwork/endpoint.go
vendored
289
vendor/src/github.com/docker/libnetwork/endpoint.go
vendored
|
@ -10,7 +10,9 @@ import (
|
|||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/options"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
|
@ -71,7 +73,9 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
|
|||
epMap["id"] = ep.id
|
||||
epMap["ep_iface"] = ep.iface
|
||||
epMap["exposed_ports"] = ep.exposedPorts
|
||||
if ep.generic != nil {
|
||||
epMap["generic"] = ep.generic
|
||||
}
|
||||
epMap["sandbox"] = ep.sandboxID
|
||||
return json.Marshal(epMap)
|
||||
}
|
||||
|
@ -98,12 +102,43 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
|
|||
cb, _ := json.Marshal(epMap["sandbox"])
|
||||
json.Unmarshal(cb, &ep.sandboxID)
|
||||
|
||||
if epMap["generic"] != nil {
|
||||
ep.generic = epMap["generic"].(map[string]interface{})
|
||||
if v, ok := epMap["generic"]; ok {
|
||||
ep.generic = v.(map[string]interface{})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) New() datastore.KVObject {
|
||||
return &endpoint{network: ep.getNetwork()}
|
||||
}
|
||||
|
||||
func (ep *endpoint) CopyTo(o datastore.KVObject) error {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
|
||||
dstEp := o.(*endpoint)
|
||||
dstEp.name = ep.name
|
||||
dstEp.id = ep.id
|
||||
dstEp.sandboxID = ep.sandboxID
|
||||
dstEp.dbIndex = ep.dbIndex
|
||||
dstEp.dbExists = ep.dbExists
|
||||
|
||||
if ep.iface != nil {
|
||||
dstEp.iface = &endpointInterface{}
|
||||
ep.iface.CopyTo(dstEp.iface)
|
||||
}
|
||||
|
||||
dstEp.exposedPorts = make([]types.TransportPort, len(ep.exposedPorts))
|
||||
copy(dstEp.exposedPorts, ep.exposedPorts)
|
||||
|
||||
dstEp.generic = options.Generic{}
|
||||
for k, v := range ep.generic {
|
||||
dstEp.generic[k] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) ID() string {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
|
@ -119,16 +154,28 @@ func (ep *endpoint) Name() string {
|
|||
}
|
||||
|
||||
func (ep *endpoint) Network() string {
|
||||
return ep.getNetwork().name
|
||||
if ep.network == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ep.network.name
|
||||
}
|
||||
|
||||
// endpoint Key structure : endpoint/network-id/endpoint-id
|
||||
func (ep *endpoint) Key() []string {
|
||||
return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id, ep.id}
|
||||
if ep.network == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []string{datastore.EndpointKeyPrefix, ep.network.id, ep.id}
|
||||
}
|
||||
|
||||
func (ep *endpoint) KeyPrefix() []string {
|
||||
return []string{datastore.EndpointKeyPrefix, ep.getNetwork().id}
|
||||
if ep.network == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return []string{datastore.EndpointKeyPrefix, ep.network.id}
|
||||
}
|
||||
|
||||
func (ep *endpoint) networkIDFromKey(key string) (string, error) {
|
||||
|
@ -188,8 +235,22 @@ func (ep *endpoint) processOptions(options ...EndpointOption) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error {
|
||||
func (ep *endpoint) getNetwork() *network {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
|
||||
return ep.network
|
||||
}
|
||||
|
||||
func (ep *endpoint) getNetworkFromStore() (*network, error) {
|
||||
if ep.network == nil {
|
||||
return nil, fmt.Errorf("invalid network object in endpoint %s", ep.Name())
|
||||
}
|
||||
|
||||
return ep.network.ctrlr.getNetworkFromStore(ep.network.id)
|
||||
}
|
||||
|
||||
func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error {
|
||||
if sbox == nil {
|
||||
return types.BadRequestErrorf("endpoint cannot be joined by nil container")
|
||||
}
|
||||
|
@ -212,15 +273,27 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
|
|||
return types.BadRequestErrorf("not a valid Sandbox interface")
|
||||
}
|
||||
|
||||
network, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network from store during join: %v", err)
|
||||
}
|
||||
|
||||
ep, err = network.getEndpointFromStore(ep.ID())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get endpoint from store during join: %v", err)
|
||||
}
|
||||
|
||||
ep.Lock()
|
||||
if ep.sandboxID != "" {
|
||||
ep.Unlock()
|
||||
return types.ForbiddenErrorf("a sandbox has already joined the endpoint")
|
||||
return types.ForbiddenErrorf("another container is attached to the same network endpoint")
|
||||
}
|
||||
ep.Unlock()
|
||||
|
||||
ep.Lock()
|
||||
ep.network = network
|
||||
ep.sandboxID = sbox.ID()
|
||||
ep.joinInfo = &endpointJoinInfo{}
|
||||
network := ep.network
|
||||
epid := ep.id
|
||||
ep.Unlock()
|
||||
defer func() {
|
||||
|
@ -232,12 +305,16 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
|
|||
}()
|
||||
|
||||
network.Lock()
|
||||
driver := network.driver
|
||||
nid := network.id
|
||||
network.Unlock()
|
||||
|
||||
ep.processOptions(options...)
|
||||
|
||||
driver, err := network.driver()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to join endpoint: %v", err)
|
||||
}
|
||||
|
||||
err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -259,15 +336,16 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err = sb.updateDNS(ep.getNetwork().enableIPv6); err != nil {
|
||||
// Watch for service records
|
||||
network.getController().watchSvcRecord(ep)
|
||||
|
||||
if err = sb.updateDNS(network.enableIPv6); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ep.isLocalScoped() {
|
||||
if err = network.ctrlr.updateToStore(ep); err != nil {
|
||||
if err = network.getController().updateToStore(ep); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
sb.Lock()
|
||||
heap.Push(&sb.endpoints, ep)
|
||||
|
@ -324,6 +402,16 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error {
|
|||
return types.BadRequestErrorf("not a valid Sandbox interface")
|
||||
}
|
||||
|
||||
n, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network from store during leave: %v", err)
|
||||
}
|
||||
|
||||
ep, err = n.getEndpointFromStore(ep.ID())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get endpoint from store during leave: %v", err)
|
||||
}
|
||||
|
||||
ep.Lock()
|
||||
sid := ep.sandboxID
|
||||
ep.Unlock()
|
||||
|
@ -339,21 +427,19 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error {
|
|||
|
||||
ep.Lock()
|
||||
ep.sandboxID = ""
|
||||
n := ep.network
|
||||
ep.network = n
|
||||
ep.Unlock()
|
||||
|
||||
n.Lock()
|
||||
c := n.ctrlr
|
||||
d := n.driver
|
||||
n.Unlock()
|
||||
|
||||
if !ep.isLocalScoped() {
|
||||
if err := c.updateToStore(ep); err != nil {
|
||||
if err := n.getController().updateToStore(ep); err != nil {
|
||||
ep.Lock()
|
||||
ep.sandboxID = sid
|
||||
ep.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
d, err := n.driver()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to leave endpoint: %v", err)
|
||||
}
|
||||
|
||||
if err := d.Leave(n.id, ep.id); err != nil {
|
||||
|
@ -364,6 +450,9 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// unwatch for service records
|
||||
n.getController().unWatchSvcRecord(ep)
|
||||
|
||||
if sb.needDefaultGW() {
|
||||
ep := sb.getEPwithoutGateway()
|
||||
if ep == nil {
|
||||
|
@ -376,45 +465,44 @@ func (ep *endpoint) sbLeave(sbox Sandbox, options ...EndpointOption) error {
|
|||
|
||||
func (ep *endpoint) Delete() error {
|
||||
var err error
|
||||
n, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network during Delete: %v", err)
|
||||
}
|
||||
|
||||
ep, err = n.getEndpointFromStore(ep.ID())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get endpoint from store during Delete: %v", err)
|
||||
}
|
||||
|
||||
ep.Lock()
|
||||
epid := ep.id
|
||||
name := ep.name
|
||||
n := ep.network
|
||||
if ep.sandboxID != "" {
|
||||
ep.Unlock()
|
||||
return &ActiveContainerError{name: name, id: epid}
|
||||
}
|
||||
n.Lock()
|
||||
ctrlr := n.ctrlr
|
||||
n.Unlock()
|
||||
ep.Unlock()
|
||||
|
||||
if !ep.isLocalScoped() {
|
||||
if err = ctrlr.deleteFromStore(ep); err != nil {
|
||||
if err = n.getEpCnt().DecEndpointCnt(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ep.dbExists = false
|
||||
if !ep.isLocalScoped() {
|
||||
if e := ctrlr.updateToStore(ep); e != nil {
|
||||
log.Warnf("failed to recreate endpoint in store %s : %v", name, e)
|
||||
}
|
||||
if e := n.getEpCnt().IncEndpointCnt(); e != nil {
|
||||
log.Warnf("failed to update network %s : %v", n.name, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Update the endpoint count in network and update it in the datastore
|
||||
n.DecEndpointCnt()
|
||||
if err = ctrlr.updateToStore(n); err != nil {
|
||||
if err = n.getController().deleteFromStore(ep); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
n.IncEndpointCnt()
|
||||
if e := ctrlr.updateToStore(n); e != nil {
|
||||
log.Warnf("failed to update network %s : %v", n.name, e)
|
||||
ep.dbExists = false
|
||||
if e := n.getController().updateToStore(ep); e != nil {
|
||||
log.Warnf("failed to recreate endpoint in store %s : %v", name, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
@ -423,6 +511,8 @@ func (ep *endpoint) Delete() error {
|
|||
return err
|
||||
}
|
||||
|
||||
ep.releaseAddress()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -433,38 +523,21 @@ func (ep *endpoint) deleteEndpoint() error {
|
|||
epid := ep.id
|
||||
ep.Unlock()
|
||||
|
||||
n.Lock()
|
||||
_, ok := n.endpoints[epid]
|
||||
if !ok {
|
||||
n.Unlock()
|
||||
return nil
|
||||
driver, err := n.driver()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete endpoint: %v", err)
|
||||
}
|
||||
|
||||
nid := n.id
|
||||
driver := n.driver
|
||||
delete(n.endpoints, epid)
|
||||
n.Unlock()
|
||||
|
||||
if err := driver.DeleteEndpoint(nid, epid); err != nil {
|
||||
if err := driver.DeleteEndpoint(n.id, epid); err != nil {
|
||||
if _, ok := err.(types.ForbiddenError); ok {
|
||||
n.Lock()
|
||||
n.endpoints[epid] = ep
|
||||
n.Unlock()
|
||||
return err
|
||||
}
|
||||
log.Warnf("driver error deleting endpoint %s : %v", name, err)
|
||||
}
|
||||
|
||||
n.updateSvcRecord(ep, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) getNetwork() *network {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
return ep.network
|
||||
}
|
||||
|
||||
func (ep *endpoint) getSandbox() (*sandbox, bool) {
|
||||
ep.Lock()
|
||||
c := ep.network.getController()
|
||||
|
@ -482,7 +555,7 @@ func (ep *endpoint) getFirstInterfaceAddress() net.IP {
|
|||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
|
||||
if ep.iface != nil {
|
||||
if ep.iface.addr != nil {
|
||||
return ep.iface.addr.IP
|
||||
}
|
||||
|
||||
|
@ -540,12 +613,94 @@ func JoinOptionPriority(ep Endpoint, prio int) EndpointOption {
|
|||
}
|
||||
}
|
||||
|
||||
func (ep *endpoint) DataScope() datastore.DataScope {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
return ep.network.dataScope
|
||||
func (ep *endpoint) DataScope() string {
|
||||
return ep.getNetwork().DataScope()
|
||||
}
|
||||
|
||||
func (ep *endpoint) isLocalScoped() bool {
|
||||
return ep.DataScope() == datastore.LocalScope
|
||||
func (ep *endpoint) assignAddress() error {
|
||||
var (
|
||||
ipam ipamapi.Ipam
|
||||
err error
|
||||
)
|
||||
|
||||
n := ep.getNetwork()
|
||||
if n.Type() == "host" || n.Type() == "null" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debugf("Assigning addresses for endpoint %s's interface on network %s", ep.Name(), n.Name())
|
||||
|
||||
ipam, err = n.getController().getIpamDriver(n.ipamType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ep.assignAddressVersion(4, ipam)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ep.assignAddressVersion(6, ipam)
|
||||
}
|
||||
|
||||
func (ep *endpoint) assignAddressVersion(ipVer int, ipam ipamapi.Ipam) error {
|
||||
var (
|
||||
poolID *string
|
||||
address **net.IPNet
|
||||
)
|
||||
|
||||
n := ep.getNetwork()
|
||||
switch ipVer {
|
||||
case 4:
|
||||
poolID = &ep.iface.v4PoolID
|
||||
address = &ep.iface.addr
|
||||
case 6:
|
||||
poolID = &ep.iface.v6PoolID
|
||||
address = &ep.iface.addrv6
|
||||
default:
|
||||
return types.InternalErrorf("incorrect ip version number passed: %d", ipVer)
|
||||
}
|
||||
|
||||
ipInfo := n.getIPInfo(ipVer)
|
||||
|
||||
// ipv6 address is not mandatory
|
||||
if len(ipInfo) == 0 && ipVer == 6 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, d := range ipInfo {
|
||||
addr, _, err := ipam.RequestAddress(d.PoolID, nil, nil)
|
||||
if err == nil {
|
||||
ep.Lock()
|
||||
*address = addr
|
||||
*poolID = d.PoolID
|
||||
ep.Unlock()
|
||||
return nil
|
||||
}
|
||||
if err != ipamapi.ErrNoAvailableIPs {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no available IPv%d addresses on this network's address pools: %s (%s)", ipVer, n.Name(), n.ID())
|
||||
}
|
||||
|
||||
func (ep *endpoint) releaseAddress() {
|
||||
n := ep.getNetwork()
|
||||
if n.Type() == "host" || n.Type() == "null" {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("Releasing addresses for endpoint %s's interface on network %s", ep.Name(), n.Name())
|
||||
|
||||
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
|
||||
}
|
||||
if err := ipam.ReleaseAddress(ep.iface.v4PoolID, ep.iface.addr.IP); err != nil {
|
||||
log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addr.IP, ep.Name(), ep.ID(), err)
|
||||
}
|
||||
if ep.iface.addrv6 != nil && ep.iface.addrv6.IP.IsGlobalUnicast() {
|
||||
if err := ipam.ReleaseAddress(ep.iface.v6PoolID, ep.iface.addrv6.IP); err != nil {
|
||||
log.Warnf("Failed to release ip address %s on delete of endpoint %s (%s): %v", ep.iface.addrv6.IP, ep.Name(), ep.ID(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
147
vendor/src/github.com/docker/libnetwork/endpoint_cnt.go
vendored
Normal file
147
vendor/src/github.com/docker/libnetwork/endpoint_cnt.go
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
)
|
||||
|
||||
type endpointCnt struct {
|
||||
n *network
|
||||
Count uint64
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
const epCntKeyPrefix = "endpoint_count"
|
||||
|
||||
func (ec *endpointCnt) Key() []string {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
return []string{epCntKeyPrefix, ec.n.id}
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) KeyPrefix() []string {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
return []string{epCntKeyPrefix, ec.n.id}
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) Value() []byte {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
b, err := json.Marshal(ec)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) SetValue(value []byte) error {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
return json.Unmarshal(value, &ec)
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) Index() uint64 {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
return ec.dbIndex
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) SetIndex(index uint64) {
|
||||
ec.Lock()
|
||||
ec.dbIndex = index
|
||||
ec.dbExists = true
|
||||
ec.Unlock()
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) Exists() bool {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
return ec.dbExists
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) Skip() bool {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
return !ec.n.persist
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) New() datastore.KVObject {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
return &endpointCnt{
|
||||
n: ec.n,
|
||||
}
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) CopyTo(o datastore.KVObject) error {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
dstEc := o.(*endpointCnt)
|
||||
dstEc.n = ec.n
|
||||
dstEc.Count = ec.Count
|
||||
dstEc.dbExists = ec.dbExists
|
||||
dstEc.dbIndex = ec.dbIndex
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) DataScope() string {
|
||||
return ec.n.DataScope()
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) EndpointCnt() uint64 {
|
||||
ec.Lock()
|
||||
defer ec.Unlock()
|
||||
|
||||
return ec.Count
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) atomicIncDecEpCnt(inc bool) error {
|
||||
retry:
|
||||
ec.Lock()
|
||||
if inc {
|
||||
ec.Count++
|
||||
} else {
|
||||
ec.Count--
|
||||
}
|
||||
ec.Unlock()
|
||||
|
||||
store := ec.n.getController().getStore(ec.DataScope())
|
||||
if store == nil {
|
||||
return fmt.Errorf("store not found for scope %s", ec.DataScope())
|
||||
}
|
||||
|
||||
if err := ec.n.getController().updateToStore(ec); err != nil {
|
||||
if err == datastore.ErrKeyModified {
|
||||
if err := store.GetObject(datastore.Key(ec.Key()...), ec); err != nil {
|
||||
return fmt.Errorf("could not update the kvobject to latest when trying to atomic add endpoint count: %v", err)
|
||||
}
|
||||
|
||||
goto retry
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) IncEndpointCnt() error {
|
||||
return ec.atomicIncDecEpCnt(true)
|
||||
}
|
||||
|
||||
func (ec *endpointCnt) DecEndpointCnt() error {
|
||||
return ec.atomicIncDecEpCnt(false)
|
||||
}
|
|
@ -2,6 +2,7 @@ package libnetwork
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
|
@ -34,26 +35,34 @@ type InterfaceInfo interface {
|
|||
MacAddress() net.HardwareAddr
|
||||
|
||||
// Address returns the IPv4 address assigned to the endpoint.
|
||||
Address() net.IPNet
|
||||
Address() *net.IPNet
|
||||
|
||||
// AddressIPv6 returns the IPv6 address assigned to the endpoint.
|
||||
AddressIPv6() net.IPNet
|
||||
AddressIPv6() *net.IPNet
|
||||
}
|
||||
|
||||
type endpointInterface struct {
|
||||
mac net.HardwareAddr
|
||||
addr net.IPNet
|
||||
addrv6 net.IPNet
|
||||
addr *net.IPNet
|
||||
addrv6 *net.IPNet
|
||||
srcName string
|
||||
dstPrefix string
|
||||
routes []*net.IPNet
|
||||
v4PoolID string
|
||||
v6PoolID string
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
|
||||
epMap := make(map[string]interface{})
|
||||
if epi.mac != nil {
|
||||
epMap["mac"] = epi.mac.String()
|
||||
}
|
||||
if epi.addr != nil {
|
||||
epMap["addr"] = epi.addr.String()
|
||||
}
|
||||
if epi.addrv6 != nil {
|
||||
epMap["addrv6"] = epi.addrv6.String()
|
||||
}
|
||||
epMap["srcName"] = epi.srcName
|
||||
epMap["dstPrefix"] = epi.dstPrefix
|
||||
var routes []string
|
||||
|
@ -61,28 +70,33 @@ func (epi *endpointInterface) MarshalJSON() ([]byte, error) {
|
|||
routes = append(routes, route.String())
|
||||
}
|
||||
epMap["routes"] = routes
|
||||
epMap["v4PoolID"] = epi.v4PoolID
|
||||
epMap["v6PoolID"] = epi.v6PoolID
|
||||
return json.Marshal(epMap)
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) UnmarshalJSON(b []byte) (err error) {
|
||||
var epMap map[string]interface{}
|
||||
if err := json.Unmarshal(b, &epMap); err != nil {
|
||||
func (epi *endpointInterface) UnmarshalJSON(b []byte) error {
|
||||
var (
|
||||
err error
|
||||
epMap map[string]interface{}
|
||||
)
|
||||
if err = json.Unmarshal(b, &epMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mac, _ := net.ParseMAC(epMap["mac"].(string))
|
||||
epi.mac = mac
|
||||
|
||||
ip, ipnet, _ := net.ParseCIDR(epMap["addr"].(string))
|
||||
if ipnet != nil {
|
||||
ipnet.IP = ip
|
||||
epi.addr = *ipnet
|
||||
if v, ok := epMap["mac"]; ok {
|
||||
if epi.mac, err = net.ParseMAC(v.(string)); err != nil {
|
||||
return types.InternalErrorf("failed to decode endpoint interface mac address after json unmarshal: %s", v.(string))
|
||||
}
|
||||
}
|
||||
if v, ok := epMap["addr"]; ok {
|
||||
if epi.addr, err = types.ParseCIDR(v.(string)); err != nil {
|
||||
return types.InternalErrorf("failed to decode endpoint interface ipv4 address after json unmarshal: %v", err)
|
||||
}
|
||||
}
|
||||
if v, ok := epMap["addrv6"]; ok {
|
||||
if epi.addrv6, err = types.ParseCIDR(v.(string)); err != nil {
|
||||
return types.InternalErrorf("failed to decode endpoint interface ipv6 address after json unmarshal: %v", err)
|
||||
}
|
||||
|
||||
ip, ipnet, _ = net.ParseCIDR(epMap["addrv6"].(string))
|
||||
if ipnet != nil {
|
||||
ipnet.IP = ip
|
||||
epi.addrv6 = *ipnet
|
||||
}
|
||||
|
||||
epi.srcName = epMap["srcName"].(string)
|
||||
|
@ -99,6 +113,24 @@ func (epi *endpointInterface) UnmarshalJSON(b []byte) (err error) {
|
|||
epi.routes = append(epi.routes, ipr)
|
||||
}
|
||||
}
|
||||
epi.v4PoolID = epMap["v4PoolID"].(string)
|
||||
epi.v6PoolID = epMap["v6PoolID"].(string)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) CopyTo(dstEpi *endpointInterface) error {
|
||||
dstEpi.mac = types.GetMacCopy(epi.mac)
|
||||
dstEpi.addr = types.GetIPNetCopy(epi.addr)
|
||||
dstEpi.addrv6 = types.GetIPNetCopy(epi.addrv6)
|
||||
dstEpi.srcName = epi.srcName
|
||||
dstEpi.dstPrefix = epi.dstPrefix
|
||||
dstEpi.v4PoolID = epi.v4PoolID
|
||||
dstEpi.v6PoolID = epi.v6PoolID
|
||||
|
||||
for _, route := range epi.routes {
|
||||
dstEpi.routes = append(dstEpi.routes, types.GetIPNetCopy(route))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -110,21 +142,38 @@ type endpointJoinInfo struct {
|
|||
}
|
||||
|
||||
func (ep *endpoint) Info() EndpointInfo {
|
||||
n, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ep, err = n.getEndpointFromStore(ep.ID())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sb, ok := ep.getSandbox()
|
||||
if !ok {
|
||||
// endpoint hasn't joined any sandbox.
|
||||
// Just return the endpoint
|
||||
return ep
|
||||
}
|
||||
|
||||
return sb.getEndpoint(ep.ID())
|
||||
}
|
||||
|
||||
func (ep *endpoint) DriverInfo() (map[string]interface{}, error) {
|
||||
ep.Lock()
|
||||
network := ep.network
|
||||
epid := ep.id
|
||||
ep.Unlock()
|
||||
n, err := ep.getNetworkFromStore()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find network in store for driver info: %v", err)
|
||||
}
|
||||
|
||||
network.Lock()
|
||||
driver := network.driver
|
||||
nid := network.id
|
||||
network.Unlock()
|
||||
driver, err := n.driver()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get driver info: %v", err)
|
||||
}
|
||||
|
||||
return driver.EndpointOperInfo(nid, epid)
|
||||
return driver.EndpointOperInfo(n.ID(), ep.ID())
|
||||
}
|
||||
|
||||
func (ep *endpoint) Iface() InterfaceInfo {
|
||||
|
@ -149,17 +198,32 @@ func (ep *endpoint) Interface() driverapi.InterfaceInfo {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (ep *endpoint) AddInterface(mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
|
||||
ep.Lock()
|
||||
defer ep.Unlock()
|
||||
|
||||
iface := &endpointInterface{
|
||||
addr: *types.GetIPNetCopy(&ipv4),
|
||||
addrv6: *types.GetIPNetCopy(&ipv6),
|
||||
func (epi *endpointInterface) SetMacAddress(mac net.HardwareAddr) error {
|
||||
if epi.mac != nil {
|
||||
return types.ForbiddenErrorf("endpoint interface MAC address present (%s). Cannot be modified with %s.", epi.mac, mac)
|
||||
}
|
||||
iface.mac = types.GetMacCopy(mac)
|
||||
if mac == nil {
|
||||
return types.BadRequestErrorf("tried to set nil MAC address to endpoint interface")
|
||||
}
|
||||
epi.mac = types.GetMacCopy(mac)
|
||||
return nil
|
||||
}
|
||||
|
||||
ep.iface = iface
|
||||
func (epi *endpointInterface) SetIPAddress(address *net.IPNet) error {
|
||||
if address.IP == nil {
|
||||
return types.BadRequestErrorf("tried to set nil IP address to endpoint interface")
|
||||
}
|
||||
if address.IP.To4() == nil {
|
||||
return setAddress(&epi.addrv6, address)
|
||||
}
|
||||
return setAddress(&epi.addr, address)
|
||||
}
|
||||
|
||||
func setAddress(ifaceAddr **net.IPNet, address *net.IPNet) error {
|
||||
if *ifaceAddr != nil {
|
||||
return types.ForbiddenErrorf("endpoint interface IP present (%s). Cannot be modified with (%s).", *ifaceAddr, address)
|
||||
}
|
||||
*ifaceAddr = types.GetIPNetCopy(address)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -167,12 +231,12 @@ func (epi *endpointInterface) MacAddress() net.HardwareAddr {
|
|||
return types.GetMacCopy(epi.mac)
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) Address() net.IPNet {
|
||||
return (*types.GetIPNetCopy(&epi.addr))
|
||||
func (epi *endpointInterface) Address() *net.IPNet {
|
||||
return types.GetIPNetCopy(epi.addr)
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) AddressIPv6() net.IPNet {
|
||||
return (*types.GetIPNetCopy(&epi.addrv6))
|
||||
func (epi *endpointInterface) AddressIPv6() *net.IPNet {
|
||||
return types.GetIPNetCopy(epi.addrv6)
|
||||
}
|
||||
|
||||
func (epi *endpointInterface) SetNames(srcName string, dstPrefix string) error {
|
||||
|
|
|
@ -129,7 +129,7 @@ type ActiveEndpointsError struct {
|
|||
}
|
||||
|
||||
func (aee *ActiveEndpointsError) Error() string {
|
||||
return fmt.Sprintf("network with name %s id %s has active endpoints", aee.name, aee.id)
|
||||
return fmt.Sprintf("network %s has active endpoints", aee.name)
|
||||
}
|
||||
|
||||
// Forbidden denotes the type of this error
|
||||
|
|
|
@ -10,13 +10,13 @@ import (
|
|||
|
||||
// Idm manages the reservation/release of numerical ids from a contiguos set
|
||||
type Idm struct {
|
||||
start uint32
|
||||
end uint32
|
||||
start uint64
|
||||
end uint64
|
||||
handle *bitseq.Handle
|
||||
}
|
||||
|
||||
// New returns an instance of id manager for a set of [start-end] numerical ids
|
||||
func New(ds datastore.DataStore, id string, start, end uint32) (*Idm, error) {
|
||||
func New(ds datastore.DataStore, id string, start, end uint64) (*Idm, error) {
|
||||
if id == "" {
|
||||
return nil, fmt.Errorf("Invalid id")
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ func New(ds datastore.DataStore, id string, start, end uint32) (*Idm, error) {
|
|||
}
|
||||
|
||||
// GetID returns the first available id in the set
|
||||
func (i *Idm) GetID() (uint32, error) {
|
||||
func (i *Idm) GetID() (uint64, error) {
|
||||
if i.handle == nil {
|
||||
return 0, fmt.Errorf("ID set is not initialized")
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func (i *Idm) GetID() (uint32, error) {
|
|||
}
|
||||
|
||||
// GetSpecificID tries to reserve the specified id
|
||||
func (i *Idm) GetSpecificID(id uint32) error {
|
||||
func (i *Idm) GetSpecificID(id uint64) error {
|
||||
if i.handle == nil {
|
||||
return fmt.Errorf("ID set is not initialized")
|
||||
}
|
||||
|
@ -55,6 +55,6 @@ func (i *Idm) GetSpecificID(id uint32) error {
|
|||
}
|
||||
|
||||
// Release releases the specified id
|
||||
func (i *Idm) Release(id uint32) {
|
||||
func (i *Idm) Release(id uint64) {
|
||||
i.handle.Unset(id - i.start)
|
||||
}
|
||||
|
|
|
@ -1,175 +0,0 @@
|
|||
// Package ipallocator defines the default IP allocator. It will move out of libnetwork as an external IPAM plugin.
|
||||
// This has been imported unchanged from Docker, besides additon of registration logic
|
||||
package ipallocator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
)
|
||||
|
||||
// allocatedMap is thread-unsafe set of allocated IP
|
||||
type allocatedMap struct {
|
||||
p map[string]struct{}
|
||||
last *big.Int
|
||||
begin *big.Int
|
||||
end *big.Int
|
||||
}
|
||||
|
||||
func newAllocatedMap(network *net.IPNet) *allocatedMap {
|
||||
firstIP, lastIP := netutils.NetworkRange(network)
|
||||
begin := big.NewInt(0).Add(ipToBigInt(firstIP), big.NewInt(1))
|
||||
end := big.NewInt(0).Sub(ipToBigInt(lastIP), big.NewInt(1))
|
||||
|
||||
return &allocatedMap{
|
||||
p: make(map[string]struct{}),
|
||||
begin: begin,
|
||||
end: end,
|
||||
last: big.NewInt(0).Sub(begin, big.NewInt(1)), // so first allocated will be begin
|
||||
}
|
||||
}
|
||||
|
||||
type networkSet map[string]*allocatedMap
|
||||
|
||||
var (
|
||||
// ErrNoAvailableIPs preformatted error
|
||||
ErrNoAvailableIPs = errors.New("no available ip addresses on network")
|
||||
// ErrIPAlreadyAllocated preformatted error
|
||||
ErrIPAlreadyAllocated = errors.New("ip already allocated")
|
||||
// ErrIPOutOfRange preformatted error
|
||||
ErrIPOutOfRange = errors.New("requested ip is out of range")
|
||||
// ErrNetworkAlreadyRegistered preformatted error
|
||||
ErrNetworkAlreadyRegistered = errors.New("network already registered")
|
||||
// ErrBadSubnet preformatted error
|
||||
ErrBadSubnet = errors.New("network does not contain specified subnet")
|
||||
)
|
||||
|
||||
// IPAllocator manages the ipam
|
||||
type IPAllocator struct {
|
||||
allocatedIPs networkSet
|
||||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
// New returns a new instance of IPAllocator
|
||||
func New() *IPAllocator {
|
||||
return &IPAllocator{networkSet{}, sync.Mutex{}}
|
||||
}
|
||||
|
||||
// RegisterSubnet registers network in global allocator with bounds
|
||||
// defined by subnet. If you want to use network range you must call
|
||||
// this method before first RequestIP, otherwise full network range will be used
|
||||
func (a *IPAllocator) RegisterSubnet(network *net.IPNet, subnet *net.IPNet) error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
nw := &net.IPNet{IP: network.IP.Mask(network.Mask), Mask: network.Mask}
|
||||
key := nw.String()
|
||||
if _, ok := a.allocatedIPs[key]; ok {
|
||||
return ErrNetworkAlreadyRegistered
|
||||
}
|
||||
|
||||
// Check that subnet is within network
|
||||
beginIP, endIP := netutils.NetworkRange(subnet)
|
||||
if !(network.Contains(beginIP) && network.Contains(endIP)) {
|
||||
return ErrBadSubnet
|
||||
}
|
||||
|
||||
n := newAllocatedMap(subnet)
|
||||
a.allocatedIPs[key] = n
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestIP requests an available ip from the given network. It
|
||||
// will return the next available ip if the ip provided is nil. If the
|
||||
// ip provided is not nil it will validate that the provided ip is available
|
||||
// for use or return an error
|
||||
func (a *IPAllocator) RequestIP(network *net.IPNet, ip net.IP) (net.IP, error) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
nw := &net.IPNet{IP: network.IP.Mask(network.Mask), Mask: network.Mask}
|
||||
key := nw.String()
|
||||
allocated, ok := a.allocatedIPs[key]
|
||||
if !ok {
|
||||
allocated = newAllocatedMap(nw)
|
||||
a.allocatedIPs[key] = allocated
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
return allocated.getNextIP()
|
||||
}
|
||||
return allocated.checkIP(ip)
|
||||
}
|
||||
|
||||
// ReleaseIP adds the provided ip back into the pool of
|
||||
// available ips to be returned for use.
|
||||
func (a *IPAllocator) ReleaseIP(network *net.IPNet, ip net.IP) error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
nw := &net.IPNet{IP: network.IP.Mask(network.Mask), Mask: network.Mask}
|
||||
if allocated, exists := a.allocatedIPs[nw.String()]; exists {
|
||||
delete(allocated.p, ip.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (allocated *allocatedMap) checkIP(ip net.IP) (net.IP, error) {
|
||||
if _, ok := allocated.p[ip.String()]; ok {
|
||||
return nil, ErrIPAlreadyAllocated
|
||||
}
|
||||
|
||||
pos := ipToBigInt(ip)
|
||||
// Verify that the IP address is within our network range.
|
||||
if pos.Cmp(allocated.begin) == -1 || pos.Cmp(allocated.end) == 1 {
|
||||
return nil, ErrIPOutOfRange
|
||||
}
|
||||
|
||||
// Register the IP.
|
||||
allocated.p[ip.String()] = struct{}{}
|
||||
|
||||
return ip, nil
|
||||
}
|
||||
|
||||
// return an available ip if one is currently available. If not,
|
||||
// return the next available ip for the network
|
||||
func (allocated *allocatedMap) getNextIP() (net.IP, error) {
|
||||
pos := big.NewInt(0).Set(allocated.last)
|
||||
allRange := big.NewInt(0).Sub(allocated.end, allocated.begin)
|
||||
for i := big.NewInt(0); i.Cmp(allRange) <= 0; i.Add(i, big.NewInt(1)) {
|
||||
pos.Add(pos, big.NewInt(1))
|
||||
if pos.Cmp(allocated.end) == 1 {
|
||||
pos.Set(allocated.begin)
|
||||
}
|
||||
if _, ok := allocated.p[bigIntToIP(pos).String()]; ok {
|
||||
continue
|
||||
}
|
||||
allocated.p[bigIntToIP(pos).String()] = struct{}{}
|
||||
allocated.last.Set(pos)
|
||||
return bigIntToIP(pos), nil
|
||||
}
|
||||
return nil, ErrNoAvailableIPs
|
||||
}
|
||||
|
||||
// Converts a 4 bytes IP into a 128 bit integer
|
||||
func ipToBigInt(ip net.IP) *big.Int {
|
||||
x := big.NewInt(0)
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return x.SetBytes(ip4)
|
||||
}
|
||||
if ip6 := ip.To16(); ip6 != nil {
|
||||
return x.SetBytes(ip6)
|
||||
}
|
||||
|
||||
logrus.Errorf("ipToBigInt: Wrong IP length! %s", ip)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Converts 128 bit integer into a 4 bytes IP address
|
||||
func bigIntToIP(v *big.Int) net.IP {
|
||||
return net.IP(v.Bytes())
|
||||
}
|
510
vendor/src/github.com/docker/libnetwork/ipam/allocator.go
vendored
Normal file
510
vendor/src/github.com/docker/libnetwork/ipam/allocator.go
vendored
Normal file
|
@ -0,0 +1,510 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/bitseq"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/ipamutils"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const (
|
||||
localAddressSpace = "LocalDefault"
|
||||
globalAddressSpace = "GlobalDefault"
|
||||
// The biggest configurable host subnets
|
||||
minNetSize = 8
|
||||
minNetSizeV6 = 64
|
||||
// datastore keyes for ipam objects
|
||||
dsConfigKey = "ipam/" + ipamapi.DefaultIPAM + "/config"
|
||||
dsDataKey = "ipam/" + ipamapi.DefaultIPAM + "/data"
|
||||
)
|
||||
|
||||
// Allocator provides per address space ipv4/ipv6 book keeping
|
||||
type Allocator struct {
|
||||
// Predefined pools for default address spaces
|
||||
predefined map[string][]*net.IPNet
|
||||
addrSpaces map[string]*addrSpace
|
||||
// stores []datastore.Datastore
|
||||
// Allocated addresses in each address space's subnet
|
||||
addresses map[SubnetKey]*bitseq.Handle
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewAllocator returns an instance of libnetwork ipam
|
||||
func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
|
||||
a := &Allocator{}
|
||||
|
||||
// Load predefined subnet pools
|
||||
a.predefined = map[string][]*net.IPNet{
|
||||
localAddressSpace: ipamutils.PredefinedBroadNetworks,
|
||||
globalAddressSpace: ipamutils.PredefinedGranularNetworks,
|
||||
}
|
||||
|
||||
// Initialize bitseq map
|
||||
a.addresses = make(map[SubnetKey]*bitseq.Handle)
|
||||
|
||||
// Initialize address spaces
|
||||
a.addrSpaces = make(map[string]*addrSpace)
|
||||
for _, aspc := range []struct {
|
||||
as string
|
||||
ds datastore.DataStore
|
||||
}{
|
||||
{localAddressSpace, lcDs},
|
||||
{globalAddressSpace, glDs},
|
||||
} {
|
||||
if aspc.ds == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
a.addrSpaces[aspc.as] = &addrSpace{
|
||||
subnets: map[SubnetKey]*PoolData{},
|
||||
id: dsConfigKey + "/" + aspc.as,
|
||||
scope: aspc.ds.Scope(),
|
||||
ds: aspc.ds,
|
||||
alloc: a,
|
||||
}
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) refresh(as string) error {
|
||||
aSpace, err := a.getAddressSpaceFromStore(as)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting pools config from store during init: %v",
|
||||
err)
|
||||
}
|
||||
|
||||
if aSpace == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := a.updateBitMasks(aSpace); err != nil {
|
||||
return fmt.Errorf("error updating bit masks during init: %v", err)
|
||||
}
|
||||
|
||||
a.Lock()
|
||||
a.addrSpaces[as] = aSpace
|
||||
a.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Allocator) updateBitMasks(aSpace *addrSpace) error {
|
||||
var inserterList []func() error
|
||||
|
||||
aSpace.Lock()
|
||||
for k, v := range aSpace.subnets {
|
||||
if v.Range == nil {
|
||||
kk := k
|
||||
vv := v
|
||||
inserterList = append(inserterList, func() error { return a.insertBitMask(kk, vv.Pool) })
|
||||
}
|
||||
}
|
||||
aSpace.Unlock()
|
||||
|
||||
// Add the bitmasks (data could come from datastore)
|
||||
if inserterList != nil {
|
||||
for _, f := range inserterList {
|
||||
if err := f(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultAddressSpaces returns the local and global default address spaces
|
||||
func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
return localAddressSpace, globalAddressSpace, nil
|
||||
}
|
||||
|
||||
// RequestPool returns an address pool along with its unique id.
|
||||
func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
||||
log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6)
|
||||
k, nw, ipr, err := a.parsePoolRequest(addressSpace, pool, subPool, v6)
|
||||
if err != nil {
|
||||
return "", nil, nil, types.InternalErrorf("failed to parse pool request for address space %q pool %q subpool %q: %v", addressSpace, pool, subPool, err)
|
||||
}
|
||||
|
||||
retry:
|
||||
if err := a.refresh(addressSpace); err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(addressSpace)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
insert, err := aSpace.updatePoolDBOnAdd(*k, nw, ipr)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
if err := a.writeToStore(aSpace); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return "", nil, nil, types.InternalErrorf("pool configuration failed because of %s", err.Error())
|
||||
}
|
||||
|
||||
goto retry
|
||||
}
|
||||
|
||||
return k.String(), nw, nil, insert()
|
||||
}
|
||||
|
||||
// ReleasePool releases the address pool identified by the passed id
|
||||
func (a *Allocator) ReleasePool(poolID string) error {
|
||||
log.Debugf("ReleasePool(%s)", poolID)
|
||||
k := SubnetKey{}
|
||||
if err := k.FromString(poolID); err != nil {
|
||||
return types.BadRequestErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
retry:
|
||||
if err := a.refresh(k.AddressSpace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remove, err := aSpace.updatePoolDBOnRemoval(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = a.writeToStore(aSpace); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return types.InternalErrorf("pool (%s) removal failed because of %v", poolID, err)
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
|
||||
return remove()
|
||||
}
|
||||
|
||||
// Given the address space, returns the local or global PoolConfig based on the
|
||||
// address space is local or global. AddressSpace locality is being registered with IPAM out of band.
|
||||
func (a *Allocator) getAddrSpace(as string) (*addrSpace, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
aSpace, ok := a.addrSpaces[as]
|
||||
if !ok {
|
||||
return nil, types.BadRequestErrorf("cannot find address space %s (most likey the backing datastore is not configured)", as)
|
||||
}
|
||||
return aSpace, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *AddressRange, error) {
|
||||
var (
|
||||
nw *net.IPNet
|
||||
ipr *AddressRange
|
||||
err error
|
||||
)
|
||||
|
||||
if addressSpace == "" {
|
||||
return nil, nil, nil, ipamapi.ErrInvalidAddressSpace
|
||||
}
|
||||
|
||||
if pool == "" && subPool != "" {
|
||||
return nil, nil, nil, ipamapi.ErrInvalidSubPool
|
||||
}
|
||||
|
||||
if pool != "" {
|
||||
if _, nw, err = net.ParseCIDR(pool); err != nil {
|
||||
return nil, nil, nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
if subPool != "" {
|
||||
if ipr, err = getAddressRange(subPool); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if nw, err = a.getPredefinedPool(addressSpace, v6); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, ipr, nil
|
||||
}
|
||||
|
||||
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 fmt.Errorf("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))
|
||||
|
||||
if ipVer == v4 {
|
||||
// Do not let broadcast address be reserved
|
||||
numAddresses--
|
||||
}
|
||||
|
||||
// Allow /64 subnet
|
||||
if ipVer == v6 && numAddresses == 0 {
|
||||
numAddresses--
|
||||
}
|
||||
|
||||
// Generate the new address masks. AddressMask content may come from datastore
|
||||
h, err := bitseq.NewHandle(dsDataKey, store, key.String(), numAddresses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Do not let network identifier address be reserved
|
||||
// Do the same for IPv6 so that bridge ip starts with XXXX...::1
|
||||
h.Set(0)
|
||||
|
||||
a.Lock()
|
||||
a.addresses[key] = h
|
||||
a.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) {
|
||||
a.Lock()
|
||||
bm, ok := a.addresses[k]
|
||||
a.Unlock()
|
||||
if !ok {
|
||||
log.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String())
|
||||
if err := a.insertBitMask(k, n); err != nil {
|
||||
return nil, fmt.Errorf("could not find bitmask in datastore for %s", k.String())
|
||||
}
|
||||
a.Lock()
|
||||
bm = a.addresses[k]
|
||||
a.Unlock()
|
||||
}
|
||||
return bm, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) getPredefineds(as string) []*net.IPNet {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
l := make([]*net.IPNet, 0, len(a.predefined[as]))
|
||||
for _, pool := range a.predefined[as] {
|
||||
l = append(l, pool)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) {
|
||||
var v ipVersion
|
||||
v = v4
|
||||
if ipV6 {
|
||||
v = v6
|
||||
}
|
||||
|
||||
if as != localAddressSpace && as != globalAddressSpace {
|
||||
return nil, fmt.Errorf("no default pool availbale for non-default addresss spaces")
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, nw := range a.getPredefineds(as) {
|
||||
if v != getAddressVersion(nw.IP) {
|
||||
continue
|
||||
}
|
||||
aSpace.Lock()
|
||||
_, ok := aSpace.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]
|
||||
aSpace.Unlock()
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return nil, types.NotFoundErrorf("could not find an available predefined network")
|
||||
}
|
||||
|
||||
// RequestAddress returns an address from the specified pool ID
|
||||
func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
|
||||
log.Debugf("RequestAddress(%s, %v, %v)", poolID, prefAddress, opts)
|
||||
k := SubnetKey{}
|
||||
if err := k.FromString(poolID); err != nil {
|
||||
return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
if err := a.refresh(k.AddressSpace); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
aSpace.Lock()
|
||||
p, ok := aSpace.subnets[k]
|
||||
if !ok {
|
||||
aSpace.Unlock()
|
||||
return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID)
|
||||
}
|
||||
|
||||
if prefAddress != nil && !p.Pool.Contains(prefAddress) {
|
||||
aSpace.Unlock()
|
||||
return nil, nil, ipamapi.ErrIPOutOfRange
|
||||
}
|
||||
|
||||
c := p
|
||||
for c.Range != nil {
|
||||
k = c.ParentKey
|
||||
c, ok = aSpace.subnets[k]
|
||||
}
|
||||
aSpace.Unlock()
|
||||
|
||||
bm, err := a.retrieveBitmask(k, c.Pool)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
|
||||
k.String(), prefAddress, poolID, err)
|
||||
}
|
||||
ip, err := a.getAddress(p.Pool, bm, prefAddress, p.Range)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &net.IPNet{IP: ip, Mask: p.Pool.Mask}, nil, nil
|
||||
}
|
||||
|
||||
// ReleaseAddress releases the address from the specified pool ID
|
||||
func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
||||
log.Debugf("ReleaseAddress(%s, %v)", poolID, address)
|
||||
k := SubnetKey{}
|
||||
if err := k.FromString(poolID); err != nil {
|
||||
return types.BadRequestErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
if err := a.refresh(k.AddressSpace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aSpace, err := a.getAddrSpace(k.AddressSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aSpace.Lock()
|
||||
p, ok := aSpace.subnets[k]
|
||||
if !ok {
|
||||
aSpace.Unlock()
|
||||
return ipamapi.ErrBadPool
|
||||
}
|
||||
|
||||
if address == nil {
|
||||
aSpace.Unlock()
|
||||
return ipamapi.ErrInvalidRequest
|
||||
}
|
||||
|
||||
if !p.Pool.Contains(address) {
|
||||
aSpace.Unlock()
|
||||
return ipamapi.ErrIPOutOfRange
|
||||
}
|
||||
|
||||
c := p
|
||||
for c.Range != nil {
|
||||
k = c.ParentKey
|
||||
c = aSpace.subnets[k]
|
||||
}
|
||||
aSpace.Unlock()
|
||||
|
||||
mask := p.Pool.Mask
|
||||
if p.Range != nil {
|
||||
mask = p.Range.Sub.Mask
|
||||
}
|
||||
|
||||
h, err := types.GetHostPartIP(address, mask)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to release address %s: %v", address.String(), err)
|
||||
}
|
||||
|
||||
bm, err := a.retrieveBitmask(k, c.Pool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
|
||||
k.String(), address, poolID, err)
|
||||
}
|
||||
|
||||
return bm.Unset(ipToUint64(h))
|
||||
}
|
||||
|
||||
func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddress net.IP, ipr *AddressRange) (net.IP, error) {
|
||||
var (
|
||||
ordinal uint64
|
||||
err error
|
||||
base *net.IPNet
|
||||
)
|
||||
|
||||
base = types.GetIPNetCopy(nw)
|
||||
|
||||
if bitmask.Unselected() <= 0 {
|
||||
return nil, ipamapi.ErrNoAvailableIPs
|
||||
}
|
||||
if ipr == nil && prefAddress == nil {
|
||||
ordinal, err = bitmask.SetAny()
|
||||
} else if prefAddress != nil {
|
||||
hostPart, e := types.GetHostPartIP(prefAddress, base.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to allocate preferred address %s: %v", prefAddress.String(), e)
|
||||
}
|
||||
ordinal = ipToUint64(types.GetMinimalIP(hostPart))
|
||||
err = bitmask.Set(ordinal)
|
||||
} else {
|
||||
base.IP = ipr.Sub.IP
|
||||
ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, ipamapi.ErrNoAvailableIPs
|
||||
}
|
||||
|
||||
// Convert IP ordinal for this subnet into IP address
|
||||
return generateAddress(ordinal, base), nil
|
||||
}
|
||||
|
||||
// DumpDatabase dumps the internal info
|
||||
func (a *Allocator) DumpDatabase() string {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
var s string
|
||||
for as, aSpace := range a.addrSpaces {
|
||||
s = fmt.Sprintf("\n\n%s Config", as)
|
||||
aSpace.Lock()
|
||||
for k, config := range aSpace.subnets {
|
||||
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config))
|
||||
}
|
||||
aSpace.Unlock()
|
||||
}
|
||||
|
||||
s = fmt.Sprintf("%s\n\nBitmasks", s)
|
||||
for k, bm := range a.addresses {
|
||||
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n\t%s: %s\n\t%d", k, bm, bm.Unselected()))
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
131
vendor/src/github.com/docker/libnetwork/ipam/store.go
vendored
Normal file
131
vendor/src/github.com/docker/libnetwork/ipam/store.go
vendored
Normal file
|
@ -0,0 +1,131 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// Key provides the Key to be used in KV Store
|
||||
func (aSpace *addrSpace) Key() []string {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
return []string{aSpace.id}
|
||||
}
|
||||
|
||||
// KeyPrefix returns the immediate parent key that can be used for tree walk
|
||||
func (aSpace *addrSpace) KeyPrefix() []string {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
return []string{dsConfigKey}
|
||||
}
|
||||
|
||||
// Value marshals the data to be stored in the KV store
|
||||
func (aSpace *addrSpace) Value() []byte {
|
||||
b, err := json.Marshal(aSpace)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to marshal ipam configured pools: %v", err)
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// SetValue unmarshalls the data from the KV store.
|
||||
func (aSpace *addrSpace) SetValue(value []byte) error {
|
||||
rc := &addrSpace{subnets: make(map[SubnetKey]*PoolData)}
|
||||
if err := json.Unmarshal(value, rc); err != nil {
|
||||
return err
|
||||
}
|
||||
aSpace.subnets = rc.subnets
|
||||
return nil
|
||||
}
|
||||
|
||||
// Index returns the latest DB Index as seen by this object
|
||||
func (aSpace *addrSpace) Index() uint64 {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
return aSpace.dbIndex
|
||||
}
|
||||
|
||||
// SetIndex method allows the datastore to store the latest DB Index into this object
|
||||
func (aSpace *addrSpace) SetIndex(index uint64) {
|
||||
aSpace.Lock()
|
||||
aSpace.dbIndex = index
|
||||
aSpace.dbExists = true
|
||||
aSpace.Unlock()
|
||||
}
|
||||
|
||||
// Exists method is true if this object has been stored in the DB.
|
||||
func (aSpace *addrSpace) Exists() bool {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
return aSpace.dbExists
|
||||
}
|
||||
|
||||
// Skip provides a way for a KV Object to avoid persisting it in the KV Store
|
||||
func (aSpace *addrSpace) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Allocator) getStore(as string) datastore.DataStore {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
if aSpace, ok := a.addrSpaces[as]; ok {
|
||||
return aSpace.ds
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
|
||||
store := a.getStore(as)
|
||||
if store == nil {
|
||||
return nil, fmt.Errorf("store for address space %s not found", as)
|
||||
}
|
||||
|
||||
pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a}
|
||||
if err := store.GetObject(datastore.Key(pc.Key()...), pc); err != nil {
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not get pools config from store: %v", err)
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) writeToStore(aSpace *addrSpace) error {
|
||||
store := aSpace.store()
|
||||
if store == nil {
|
||||
return fmt.Errorf("invalid store while trying to write %s address space", aSpace.DataScope())
|
||||
}
|
||||
|
||||
err := store.PutObjectAtomic(aSpace)
|
||||
if err == datastore.ErrKeyModified {
|
||||
return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *Allocator) deleteFromStore(aSpace *addrSpace) error {
|
||||
store := aSpace.store()
|
||||
if store == nil {
|
||||
return fmt.Errorf("invalid store while trying to delete %s address space", aSpace.DataScope())
|
||||
}
|
||||
|
||||
return store.DeleteObjectAtomic(aSpace)
|
||||
}
|
||||
|
||||
// DataScope method returns the storage scope of the datastore
|
||||
func (aSpace *addrSpace) DataScope() string {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
return aSpace.scope
|
||||
}
|
359
vendor/src/github.com/docker/libnetwork/ipam/structures.go
vendored
Normal file
359
vendor/src/github.com/docker/libnetwork/ipam/structures.go
vendored
Normal file
|
@ -0,0 +1,359 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// SubnetKey is the pointer to the configured pools in each address space
|
||||
type SubnetKey struct {
|
||||
AddressSpace string
|
||||
Subnet string
|
||||
ChildSubnet string
|
||||
}
|
||||
|
||||
// PoolData contains the configured pool data
|
||||
type PoolData struct {
|
||||
ParentKey SubnetKey
|
||||
Pool *net.IPNet
|
||||
Range *AddressRange `json:",omitempty"`
|
||||
RefCount int
|
||||
}
|
||||
|
||||
// addrSpace contains the pool configurations for the address space
|
||||
type addrSpace struct {
|
||||
subnets map[SubnetKey]*PoolData
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
id string
|
||||
scope string
|
||||
ds datastore.DataStore
|
||||
alloc *Allocator
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// AddressRange specifies first and last ip ordinal which
|
||||
// identify a range in a a pool of addresses
|
||||
type AddressRange struct {
|
||||
Sub *net.IPNet
|
||||
Start, End uint64
|
||||
}
|
||||
|
||||
// String returns the string form of the AddressRange object
|
||||
func (r *AddressRange) String() string {
|
||||
return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Range object
|
||||
func (r *AddressRange) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"Sub": r.Sub.String(),
|
||||
"Start": r.Start,
|
||||
"End": r.End,
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the Range object
|
||||
func (r *AddressRange) UnmarshalJSON(data []byte) error {
|
||||
m := map[string]interface{}{}
|
||||
err := json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Start = uint64(m["Start"].(float64))
|
||||
r.End = uint64(m["End"].(float64))
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string form of the SubnetKey object
|
||||
func (s *SubnetKey) String() string {
|
||||
k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
|
||||
if s.ChildSubnet != "" {
|
||||
k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
// FromString populate the SubnetKey object reading it from string
|
||||
func (s *SubnetKey) FromString(str string) error {
|
||||
if str == "" || !strings.Contains(str, "/") {
|
||||
return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
||||
}
|
||||
|
||||
p := strings.Split(str, "/")
|
||||
if len(p) != 3 && len(p) != 5 {
|
||||
return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
||||
}
|
||||
s.AddressSpace = p[0]
|
||||
s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
|
||||
if len(p) == 5 {
|
||||
s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string form of the PoolData object
|
||||
func (p *PoolData) String() string {
|
||||
return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d",
|
||||
p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the PoolData object
|
||||
func (p *PoolData) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"ParentKey": p.ParentKey,
|
||||
"RefCount": p.RefCount,
|
||||
}
|
||||
if p.Pool != nil {
|
||||
m["Pool"] = p.Pool.String()
|
||||
}
|
||||
if p.Range != nil {
|
||||
m["Range"] = p.Range
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the PoolData object
|
||||
func (p *PoolData) UnmarshalJSON(data []byte) error {
|
||||
var (
|
||||
err error
|
||||
t struct {
|
||||
ParentKey SubnetKey
|
||||
Pool string
|
||||
Range *AddressRange `json:",omitempty"`
|
||||
RefCount int
|
||||
}
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(data, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ParentKey = t.ParentKey
|
||||
p.Range = t.Range
|
||||
p.RefCount = t.RefCount
|
||||
if t.Pool != "" {
|
||||
if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the addrSpace object
|
||||
func (aSpace *addrSpace) MarshalJSON() ([]byte, error) {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
m := map[string]interface{}{
|
||||
"Scope": string(aSpace.scope),
|
||||
}
|
||||
|
||||
if aSpace.subnets != nil {
|
||||
s := map[string]*PoolData{}
|
||||
for k, v := range aSpace.subnets {
|
||||
s[k.String()] = v
|
||||
}
|
||||
m["Subnets"] = s
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the addrSpace object
|
||||
func (aSpace *addrSpace) UnmarshalJSON(data []byte) error {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
m := map[string]interface{}{}
|
||||
err := json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aSpace.scope = datastore.LocalScope
|
||||
s := m["Scope"].(string)
|
||||
if s == string(datastore.GlobalScope) {
|
||||
aSpace.scope = datastore.GlobalScope
|
||||
}
|
||||
|
||||
if v, ok := m["Subnets"]; ok {
|
||||
sb, _ := json.Marshal(v)
|
||||
var s map[string]*PoolData
|
||||
err := json.Unmarshal(sb, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ks, v := range s {
|
||||
k := SubnetKey{}
|
||||
k.FromString(ks)
|
||||
aSpace.subnets[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CopyTo deep copies the pool data to the destination pooldata
|
||||
func (p *PoolData) CopyTo(dstP *PoolData) error {
|
||||
dstP.ParentKey = p.ParentKey
|
||||
dstP.Pool = types.GetIPNetCopy(p.Pool)
|
||||
|
||||
if p.Range != nil {
|
||||
dstP.Range = &AddressRange{}
|
||||
dstP.Range.Sub = types.GetIPNetCopy(p.Range.Sub)
|
||||
dstP.Range.Start = p.Range.Start
|
||||
dstP.Range.End = p.Range.End
|
||||
}
|
||||
|
||||
dstP.RefCount = p.RefCount
|
||||
return nil
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) CopyTo(o datastore.KVObject) error {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
dstAspace := o.(*addrSpace)
|
||||
|
||||
dstAspace.id = aSpace.id
|
||||
dstAspace.ds = aSpace.ds
|
||||
dstAspace.alloc = aSpace.alloc
|
||||
dstAspace.scope = aSpace.scope
|
||||
dstAspace.dbIndex = aSpace.dbIndex
|
||||
dstAspace.dbExists = aSpace.dbExists
|
||||
|
||||
dstAspace.subnets = make(map[SubnetKey]*PoolData)
|
||||
for k, v := range aSpace.subnets {
|
||||
dstAspace.subnets[k] = &PoolData{}
|
||||
v.CopyTo(dstAspace.subnets[k])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) New() datastore.KVObject {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
return &addrSpace{
|
||||
id: aSpace.id,
|
||||
ds: aSpace.ds,
|
||||
alloc: aSpace.alloc,
|
||||
scope: aSpace.scope,
|
||||
}
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
// Check if already allocated
|
||||
if p, ok := aSpace.subnets[k]; ok {
|
||||
aSpace.incRefCount(p, 1)
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
// If master pool, check for overlap
|
||||
if ipr == nil {
|
||||
if aSpace.contains(k.AddressSpace, nw) {
|
||||
return nil, ipamapi.ErrPoolOverlap
|
||||
}
|
||||
// This is a new master pool, add it along with corresponding bitmask
|
||||
aSpace.subnets[k] = &PoolData{Pool: nw, RefCount: 1}
|
||||
return func() error { return aSpace.alloc.insertBitMask(k, nw) }, nil
|
||||
}
|
||||
|
||||
// This is a new non-master pool
|
||||
p := &PoolData{
|
||||
ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet},
|
||||
Pool: nw,
|
||||
Range: ipr,
|
||||
RefCount: 1,
|
||||
}
|
||||
aSpace.subnets[k] = p
|
||||
|
||||
// Look for parent pool
|
||||
pp, ok := aSpace.subnets[p.ParentKey]
|
||||
if ok {
|
||||
aSpace.incRefCount(pp, 1)
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
// Parent pool does not exist, add it along with corresponding bitmask
|
||||
aSpace.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1}
|
||||
return func() error { return aSpace.alloc.insertBitMask(p.ParentKey, nw) }, nil
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
p, ok := aSpace.subnets[k]
|
||||
if !ok {
|
||||
return nil, ipamapi.ErrBadPool
|
||||
}
|
||||
|
||||
aSpace.incRefCount(p, -1)
|
||||
|
||||
c := p
|
||||
for ok {
|
||||
if c.RefCount == 0 {
|
||||
delete(aSpace.subnets, k)
|
||||
if c.Range == nil {
|
||||
return func() error {
|
||||
bm, err := aSpace.alloc.retrieveBitmask(k, c.Pool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
||||
}
|
||||
return bm.Destroy()
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
k = c.ParentKey
|
||||
c, ok = aSpace.subnets[k]
|
||||
}
|
||||
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) incRefCount(p *PoolData, delta int) {
|
||||
c := p
|
||||
ok := true
|
||||
for ok {
|
||||
c.RefCount += delta
|
||||
c, ok = aSpace.subnets[c.ParentKey]
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether the passed subnet is a superset or subset of any of the subset in this config db
|
||||
func (aSpace *addrSpace) contains(space string, nw *net.IPNet) bool {
|
||||
for k, v := range aSpace.subnets {
|
||||
if space == k.AddressSpace && k.ChildSubnet == "" {
|
||||
if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (aSpace *addrSpace) store() datastore.DataStore {
|
||||
aSpace.Lock()
|
||||
defer aSpace.Unlock()
|
||||
|
||||
return aSpace.ds
|
||||
}
|
81
vendor/src/github.com/docker/libnetwork/ipam/utils.go
vendored
Normal file
81
vendor/src/github.com/docker/libnetwork/ipam/utils.go
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
type ipVersion int
|
||||
|
||||
const (
|
||||
v4 = 4
|
||||
v6 = 6
|
||||
)
|
||||
|
||||
func getAddressRange(pool string) (*AddressRange, error) {
|
||||
ip, nw, err := net.ParseCIDR(pool)
|
||||
if err != nil {
|
||||
return nil, ipamapi.ErrInvalidSubPool
|
||||
}
|
||||
lIP, e := types.GetHostPartIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
|
||||
}
|
||||
bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
|
||||
}
|
||||
hIP, e := types.GetHostPartIP(bIP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
|
||||
}
|
||||
nw.IP = ip
|
||||
return &AddressRange{nw, ipToUint64(types.GetMinimalIP(lIP)), ipToUint64(types.GetMinimalIP(hIP))}, nil
|
||||
}
|
||||
|
||||
// It generates the ip address in the passed subnet specified by
|
||||
// the passed host address ordinal
|
||||
func generateAddress(ordinal uint64, network *net.IPNet) net.IP {
|
||||
var address [16]byte
|
||||
|
||||
// Get network portion of IP
|
||||
if getAddressVersion(network.IP) == v4 {
|
||||
copy(address[:], network.IP.To4())
|
||||
} else {
|
||||
copy(address[:], network.IP)
|
||||
}
|
||||
|
||||
end := len(network.Mask)
|
||||
addIntToIP(address[:end], ordinal)
|
||||
|
||||
return net.IP(address[:end])
|
||||
}
|
||||
|
||||
func getAddressVersion(ip net.IP) ipVersion {
|
||||
if ip.To4() == nil {
|
||||
return v6
|
||||
}
|
||||
return v4
|
||||
}
|
||||
|
||||
// Adds the ordinal IP to the current array
|
||||
// 192.168.0.0 + 53 => 192.168.53
|
||||
func addIntToIP(array []byte, ordinal uint64) {
|
||||
for i := len(array) - 1; i >= 0; i-- {
|
||||
array[i] |= (byte)(ordinal & 0xff)
|
||||
ordinal >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an ordinal to the respective IP address
|
||||
func ipToUint64(ip []byte) (value uint64) {
|
||||
cip := types.GetMinimalIP(ip)
|
||||
for i := 0; i < len(cip); i++ {
|
||||
j := len(cip) - 1 - i
|
||||
value += uint64(cip[i]) << uint(j*8)
|
||||
}
|
||||
return value
|
||||
}
|
72
vendor/src/github.com/docker/libnetwork/ipamapi/contract.go
vendored
Normal file
72
vendor/src/github.com/docker/libnetwork/ipamapi/contract.go
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Package ipamapi specifies the contract the IPAM service (built-in or remote) needs to satisfy.
|
||||
package ipamapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
/********************
|
||||
* IPAM plugin types
|
||||
********************/
|
||||
|
||||
const (
|
||||
// DefaultIPAM is the name of the built-in default ipam driver
|
||||
DefaultIPAM = "default"
|
||||
// PluginEndpointType represents the Endpoint Type used by Plugin system
|
||||
PluginEndpointType = "IpamDriver"
|
||||
)
|
||||
|
||||
// Callback provides a Callback interface for registering an IPAM instance into LibNetwork
|
||||
type Callback interface {
|
||||
// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a ipam instance
|
||||
RegisterIpamDriver(name string, driver Ipam) error
|
||||
}
|
||||
|
||||
/**************
|
||||
* IPAM Errors
|
||||
**************/
|
||||
|
||||
// Weel-known errors returned by IPAM
|
||||
var (
|
||||
ErrInvalidIpamService = errors.New("Invalid IPAM Service")
|
||||
ErrInvalidIpamConfigService = errors.New("Invalid IPAM Config Service")
|
||||
ErrIpamNotAvailable = errors.New("IPAM Service not available")
|
||||
ErrIpamInternalError = errors.New("IPAM Internal Error")
|
||||
ErrInvalidAddressSpace = errors.New("Invalid Address Space")
|
||||
ErrInvalidPool = errors.New("Invalid Address Pool")
|
||||
ErrInvalidSubPool = errors.New("Invalid Address SubPool")
|
||||
ErrInvalidRequest = errors.New("Invalid Request")
|
||||
ErrPoolNotFound = errors.New("Address Pool not found")
|
||||
ErrOverlapPool = errors.New("Address pool overlaps with existing pool on this address space")
|
||||
ErrNoAvailablePool = errors.New("No available pool")
|
||||
ErrNoAvailableIPs = errors.New("No available addresses on this pool")
|
||||
ErrIPAlreadyAllocated = errors.New("Address already in use")
|
||||
ErrIPOutOfRange = errors.New("Requested address is out of range")
|
||||
ErrPoolOverlap = errors.New("Pool overlaps with other one on this address space")
|
||||
ErrBadPool = errors.New("Address space does not contain specified address pool")
|
||||
)
|
||||
|
||||
/*******************************
|
||||
* IPAM Service Interface
|
||||
*******************************/
|
||||
|
||||
// Ipam represents the interface the IPAM service plugins must implement
|
||||
// in order to allow injection/modification of IPAM database.
|
||||
type Ipam interface {
|
||||
// GetDefaultAddressSpaces returns the default local and global address spaces for this ipam
|
||||
GetDefaultAddressSpaces() (string, string, error)
|
||||
// RequestPool returns an address pool along with its unique id. Address space is a mandatory field
|
||||
// which denotes a set of non-overlapping pools. pool describes the pool of addresses in CIDR notation.
|
||||
// subpool indicates a smaller range of addresses from the pool, for now it is specified in CIDR notation.
|
||||
// Both pool and subpool are non mandatory fields. When they are not specified, Ipam driver may choose to
|
||||
// return a self chosen pool for this request. In such case the v6 flag needs to be set appropriately so
|
||||
// that the driver would return the expected ip version pool.
|
||||
RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error)
|
||||
// ReleasePool releases the address pool identified by the passed id
|
||||
ReleasePool(poolID string) error
|
||||
// Request address from the specified pool ID. Input options or preferred IP can be passed.
|
||||
RequestAddress(string, net.IP, map[string]string) (*net.IPNet, map[string]string, error)
|
||||
// Release the address from the specified pool ID
|
||||
ReleaseAddress(string, net.IP) error
|
||||
}
|
35
vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin.go
vendored
Normal file
35
vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin.go
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipam"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
)
|
||||
|
||||
// Init registers the built-in ipam service with libnetwork
|
||||
func Init(ic ipamapi.Callback, l, g interface{}) error {
|
||||
var (
|
||||
ok bool
|
||||
localDs, globalDs datastore.DataStore
|
||||
)
|
||||
|
||||
if l != nil {
|
||||
if localDs, ok = l.(datastore.DataStore); !ok {
|
||||
return fmt.Errorf("incorrect local datastore passed to built-in ipam init")
|
||||
}
|
||||
}
|
||||
|
||||
if g != nil {
|
||||
if globalDs, ok = g.(datastore.DataStore); !ok {
|
||||
return fmt.Errorf("incorrect global datastore passed to built-in ipam init")
|
||||
}
|
||||
}
|
||||
a, err := ipam.NewAllocator(localDs, globalDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ic.RegisterIpamDriver(ipamapi.DefaultIPAM, a)
|
||||
}
|
77
vendor/src/github.com/docker/libnetwork/ipams/remote/api/api.go
vendored
Normal file
77
vendor/src/github.com/docker/libnetwork/ipams/remote/api/api.go
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Package api defines the data structure to be used in the request/response
|
||||
// messages between libnetwork and the remote ipam plugin
|
||||
package api
|
||||
|
||||
// Response is the basic response structure used in all responses
|
||||
type Response struct {
|
||||
Error string
|
||||
}
|
||||
|
||||
// IsSuccess returns wheter the plugin response is successful
|
||||
func (r *Response) IsSuccess() bool {
|
||||
return r.Error == ""
|
||||
}
|
||||
|
||||
// GetError returns the error from the response, if any.
|
||||
func (r *Response) GetError() string {
|
||||
return r.Error
|
||||
}
|
||||
|
||||
// GetAddressSpacesResponse is the response to the ``get default address spaces`` request message
|
||||
type GetAddressSpacesResponse struct {
|
||||
Response
|
||||
LocalDefaultAddressSpace string
|
||||
GlobalDefaultAddressSpace string
|
||||
}
|
||||
|
||||
// RequestPoolRequest represents the expected data in a ``request address pool`` request message
|
||||
type RequestPoolRequest struct {
|
||||
AddressSpace string
|
||||
Pool string
|
||||
SubPool string
|
||||
Options map[string]string
|
||||
V6 bool
|
||||
}
|
||||
|
||||
// RequestPoolResponse represents the response message to a ``request address pool`` request
|
||||
type RequestPoolResponse struct {
|
||||
Response
|
||||
PoolID string
|
||||
Pool string // CIDR format
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
// ReleasePoolRequest represents the expected data in a ``release address pool`` request message
|
||||
type ReleasePoolRequest struct {
|
||||
PoolID string
|
||||
}
|
||||
|
||||
// ReleasePoolResponse represents the response message to a ``release address pool`` request
|
||||
type ReleasePoolResponse struct {
|
||||
Response
|
||||
}
|
||||
|
||||
// RequestAddressRequest represents the expected data in a ``request address`` request message
|
||||
type RequestAddressRequest struct {
|
||||
PoolID string
|
||||
Address string
|
||||
Options map[string]string
|
||||
}
|
||||
|
||||
// RequestAddressResponse represents the expected data in the response message to a ``request address`` request
|
||||
type RequestAddressResponse struct {
|
||||
Response
|
||||
Address string // in CIDR format
|
||||
Data map[string]string
|
||||
}
|
||||
|
||||
// ReleaseAddressRequest represents the expected data in a ``release address`` request message
|
||||
type ReleaseAddressRequest struct {
|
||||
PoolID string
|
||||
Address string
|
||||
}
|
||||
|
||||
// ReleaseAddressResponse represents the response message to a ``release address`` request
|
||||
type ReleaseAddressResponse struct {
|
||||
Response
|
||||
}
|
103
vendor/src/github.com/docker/libnetwork/ipams/remote/remote.go
vendored
Normal file
103
vendor/src/github.com/docker/libnetwork/ipams/remote/remote.go
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package remote
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
type allocator struct {
|
||||
endpoint *plugins.Client
|
||||
name string
|
||||
}
|
||||
|
||||
// PluginResponse is the interface for the plugin request responses
|
||||
type PluginResponse interface {
|
||||
IsSuccess() bool
|
||||
GetError() string
|
||||
}
|
||||
|
||||
func newAllocator(name string, client *plugins.Client) ipamapi.Ipam {
|
||||
a := &allocator{name: name, endpoint: client}
|
||||
return a
|
||||
}
|
||||
|
||||
// Init registers a remote ipam when its plugin is activated
|
||||
func Init(cb ipamapi.Callback, l, g interface{}) error {
|
||||
plugins.Handle(ipamapi.PluginEndpointType, func(name string, client *plugins.Client) {
|
||||
if err := cb.RegisterIpamDriver(name, newAllocator(name, client)); err != nil {
|
||||
log.Errorf("error registering remote ipam %s due to %v", name, err)
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *allocator) call(methodName string, arg interface{}, retVal PluginResponse) error {
|
||||
method := ipamapi.PluginEndpointType + "." + methodName
|
||||
err := a.endpoint.Call(method, arg, retVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !retVal.IsSuccess() {
|
||||
return fmt.Errorf("remote: %s", retVal.GetError())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultAddressSpaces returns the local and global default address spaces
|
||||
func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
res := &api.GetAddressSpacesResponse{}
|
||||
if err := a.call("GetDefaultAddressSpaces", nil, res); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return res.LocalDefaultAddressSpace, res.GlobalDefaultAddressSpace, nil
|
||||
}
|
||||
|
||||
// RequestPool requests an address pool in the specified address space
|
||||
func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
|
||||
req := &api.RequestPoolRequest{AddressSpace: addressSpace, Pool: pool, SubPool: subPool, Options: options, V6: v6}
|
||||
res := &api.RequestPoolResponse{}
|
||||
if err := a.call("RequestPool", req, res); err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
retPool, err := types.ParseCIDR(res.Pool)
|
||||
return res.PoolID, retPool, res.Data, err
|
||||
}
|
||||
|
||||
// ReleasePool removes an address pool from the specified address space
|
||||
func (a *allocator) ReleasePool(poolID string) error {
|
||||
req := &api.ReleasePoolRequest{PoolID: poolID}
|
||||
res := &api.ReleasePoolResponse{}
|
||||
return a.call("ReleasePool", req, res)
|
||||
}
|
||||
|
||||
// RequestAddress requests an address from the address pool
|
||||
func (a *allocator) RequestAddress(poolID string, address net.IP, options map[string]string) (*net.IPNet, map[string]string, error) {
|
||||
var prefAddress string
|
||||
if address != nil {
|
||||
prefAddress = address.String()
|
||||
}
|
||||
req := &api.RequestAddressRequest{PoolID: poolID, Address: prefAddress, Options: options}
|
||||
res := &api.RequestAddressResponse{}
|
||||
if err := a.call("RequestAddress", req, res); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
retAddress, err := types.ParseCIDR(res.Address)
|
||||
return retAddress, res.Data, err
|
||||
}
|
||||
|
||||
// ReleaseAddress releases the address from the specified address pool
|
||||
func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
|
||||
var relAddress string
|
||||
if address != nil {
|
||||
relAddress = address.String()
|
||||
}
|
||||
req := &api.ReleaseAddressRequest{PoolID: poolID, Address: relAddress}
|
||||
res := &api.ReleaseAddressResponse{}
|
||||
return a.call("ReleaseAddress", req, res)
|
||||
}
|
42
vendor/src/github.com/docker/libnetwork/ipamutils/utils.go
vendored
Normal file
42
vendor/src/github.com/docker/libnetwork/ipamutils/utils.go
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
// Package ipamutils provides utililty functions for ipam management
|
||||
package ipamutils
|
||||
|
||||
import "net"
|
||||
|
||||
var (
|
||||
// PredefinedBroadNetworks contains a list of 31 IPv4 private networks with host size 16 and 12
|
||||
// (172.17-31.x.x/16, 192.168.x.x/20) which do not overlap with the networks in `PredefinedGranularNetworks`
|
||||
PredefinedBroadNetworks []*net.IPNet
|
||||
// PredefinedGranularNetworks contains a list of 64K IPv4 private networks with host size 8
|
||||
// (10.x.x.x/24) which do not overlap with the networks in `PredefinedBroadNetworks`
|
||||
PredefinedGranularNetworks []*net.IPNet
|
||||
)
|
||||
|
||||
func init() {
|
||||
PredefinedBroadNetworks = initBroadPredefinedNetworks()
|
||||
PredefinedGranularNetworks = initGranularPredefinedNetworks()
|
||||
}
|
||||
|
||||
func initBroadPredefinedNetworks() []*net.IPNet {
|
||||
pl := make([]*net.IPNet, 0, 31)
|
||||
mask := []byte{255, 255, 0, 0}
|
||||
for i := 17; i < 32; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
|
||||
}
|
||||
mask20 := []byte{255, 255, 240, 0}
|
||||
for i := 0; i < 16; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i << 4), 0}, Mask: mask20})
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
func initGranularPredefinedNetworks() []*net.IPNet {
|
||||
pl := make([]*net.IPNet, 0, 256*256)
|
||||
mask := []byte{255, 255, 255, 0}
|
||||
for i := 0; i < 256; i++ {
|
||||
for j := 0; j < 256; j++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
|
||||
}
|
||||
}
|
||||
return pl
|
||||
}
|
71
vendor/src/github.com/docker/libnetwork/ipamutils/utils_linux.go
vendored
Normal file
71
vendor/src/github.com/docker/libnetwork/ipamutils/utils_linux.go
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Package ipamutils provides utililty functions for ipam management
|
||||
package ipamutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"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
|
||||
)
|
||||
|
||||
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")
|
||||
}
|
22
vendor/src/github.com/docker/libnetwork/ipamutils/utils_windows.go
vendored
Normal file
22
vendor/src/github.com/docker/libnetwork/ipamutils/utils_windows.go
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Package ipamutils provides utililty functions for ipam management
|
||||
package ipamutils
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
return nil, nil, types.NotImplementedErrorf("not supported on windows")
|
||||
}
|
||||
|
||||
// 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 nil, types.NotImplementedErrorf("not supported on windows")
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package netlabel
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// Prefix constant marks the reserved label space for libnetwork
|
||||
|
@ -9,6 +11,10 @@ const (
|
|||
// DriverPrefix constant marks the reserved label space for libnetwork drivers
|
||||
DriverPrefix = Prefix + ".driver"
|
||||
|
||||
// DriverPrivatePrefix constant marks the reserved label space
|
||||
// for internal libnetwork drivers
|
||||
DriverPrivatePrefix = DriverPrefix + ".private"
|
||||
|
||||
// GenericData constant that helps to identify an option as a Generic constant
|
||||
GenericData = Prefix + ".generic"
|
||||
|
||||
|
@ -18,38 +24,83 @@ const (
|
|||
// MacAddress constant represents Mac Address config of a Container
|
||||
MacAddress = Prefix + ".endpoint.macaddress"
|
||||
|
||||
// ExposedPorts constant represents exposedports of a Container
|
||||
// ExposedPorts constant represents the container's Exposed Ports
|
||||
ExposedPorts = Prefix + ".endpoint.exposedports"
|
||||
|
||||
//EnableIPv6 constant represents enabling IPV6 at network level
|
||||
EnableIPv6 = Prefix + ".enable_ipv6"
|
||||
|
||||
// KVProvider constant represents the KV provider backend
|
||||
KVProvider = DriverPrefix + ".kv_provider"
|
||||
|
||||
// KVProviderURL constant represents the KV provider URL
|
||||
KVProviderURL = DriverPrefix + ".kv_provider_url"
|
||||
|
||||
// KVProviderConfig constant represents the KV provider Config
|
||||
KVProviderConfig = DriverPrefix + ".kv_provider_config"
|
||||
// DriverMTU constant represents the MTU size for the network driver
|
||||
DriverMTU = DriverPrefix + ".mtu"
|
||||
|
||||
// OverlayBindInterface constant represents overlay driver bind interface
|
||||
OverlayBindInterface = DriverPrefix + ".overlay.bind_interface"
|
||||
|
||||
// OverlayNeighborIP constant represents overlay driver neighbor IP
|
||||
OverlayNeighborIP = DriverPrefix + ".overlay.neighbor_ip"
|
||||
|
||||
// Gateway represents the gateway for the network
|
||||
Gateway = Prefix + ".gateway"
|
||||
)
|
||||
|
||||
// Key extracts the key portion of the label
|
||||
func Key(label string) string {
|
||||
kv := strings.SplitN(label, "=", 2)
|
||||
var (
|
||||
// GlobalKVProvider constant represents the KV provider backend
|
||||
GlobalKVProvider = MakeKVProvider("global")
|
||||
|
||||
return kv[0]
|
||||
// GlobalKVProviderURL constant represents the KV provider URL
|
||||
GlobalKVProviderURL = MakeKVProviderURL("global")
|
||||
|
||||
// GlobalKVProviderConfig constant represents the KV provider Config
|
||||
GlobalKVProviderConfig = MakeKVProviderConfig("global")
|
||||
|
||||
// LocalKVProvider constant represents the KV provider backend
|
||||
LocalKVProvider = MakeKVProvider("local")
|
||||
|
||||
// LocalKVProviderURL constant represents the KV provider URL
|
||||
LocalKVProviderURL = MakeKVProviderURL("local")
|
||||
|
||||
// LocalKVProviderConfig constant represents the KV provider Config
|
||||
LocalKVProviderConfig = MakeKVProviderConfig("local")
|
||||
)
|
||||
|
||||
// MakeKVProvider returns the kvprovider label for the scope
|
||||
func MakeKVProvider(scope string) string {
|
||||
return DriverPrivatePrefix + scope + "kv_provider"
|
||||
}
|
||||
|
||||
// MakeKVProviderURL returns the kvprovider url label for the scope
|
||||
func MakeKVProviderURL(scope string) string {
|
||||
return DriverPrivatePrefix + scope + "kv_provider_url"
|
||||
}
|
||||
|
||||
// MakeKVProviderConfig returns the kvprovider config label for the scope
|
||||
func MakeKVProviderConfig(scope string) string {
|
||||
return DriverPrivatePrefix + scope + "kv_provider_config"
|
||||
}
|
||||
|
||||
// Key extracts the key portion of the label
|
||||
func Key(label string) (key string) {
|
||||
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
|
||||
key = kv[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Value extracts the value portion of the label
|
||||
func Value(label string) string {
|
||||
kv := strings.SplitN(label, "=", 2)
|
||||
|
||||
return kv[1]
|
||||
func Value(label string) (value string) {
|
||||
if kv := strings.SplitN(label, "=", 2); len(kv) > 1 {
|
||||
value = kv[1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KeyValue decomposes the label in the (key,value) pair
|
||||
func KeyValue(label string) (key string, value string) {
|
||||
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
|
||||
key = kv[0]
|
||||
if len(kv) > 1 {
|
||||
value = kv[1]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
822
vendor/src/github.com/docker/libnetwork/network.go
vendored
822
vendor/src/github.com/docker/libnetwork/network.go
vendored
File diff suppressed because it is too large
Load diff
|
@ -156,7 +156,7 @@ func (i *nwIface) Remove() error {
|
|||
}
|
||||
|
||||
// Returns the sandbox's side veth interface statistics
|
||||
func (i *nwIface) Statistics() (*InterfaceStatistics, error) {
|
||||
func (i *nwIface) Statistics() (*types.InterfaceStatistics, error) {
|
||||
i.Lock()
|
||||
n := i.ns
|
||||
i.Unlock()
|
||||
|
@ -165,7 +165,7 @@ func (i *nwIface) Statistics() (*InterfaceStatistics, error) {
|
|||
path := n.path
|
||||
n.Unlock()
|
||||
|
||||
s := &InterfaceStatistics{}
|
||||
s := &types.InterfaceStatistics{}
|
||||
|
||||
err := nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
|
||||
// For some reason ioutil.ReadFile(netStatsFile) reads the file in
|
||||
|
@ -356,7 +356,7 @@ const (
|
|||
base = "[ ]*%s:([ ]+[0-9]+){16}"
|
||||
)
|
||||
|
||||
func scanInterfaceStats(data, ifName string, i *InterfaceStatistics) error {
|
||||
func scanInterfaceStats(data, ifName string, i *types.InterfaceStatistics) error {
|
||||
var (
|
||||
bktStr string
|
||||
bkt uint64
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
package osl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/types"
|
||||
|
@ -150,22 +149,5 @@ type Interface interface {
|
|||
Remove() error
|
||||
|
||||
// Statistics returns the statistics for this interface
|
||||
Statistics() (*InterfaceStatistics, error)
|
||||
}
|
||||
|
||||
// InterfaceStatistics represents the interface's statistics
|
||||
type InterfaceStatistics struct {
|
||||
RxBytes uint64
|
||||
RxPackets uint64
|
||||
RxErrors uint64
|
||||
RxDropped uint64
|
||||
TxBytes uint64
|
||||
TxPackets uint64
|
||||
TxErrors uint64
|
||||
TxDropped uint64
|
||||
}
|
||||
|
||||
func (is *InterfaceStatistics) String() string {
|
||||
return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
|
||||
is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
|
||||
Statistics() (*types.InterfaceStatistics, error)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
)
|
||||
|
||||
// IPLocalhost is a regex patter for localhost IP address range.
|
||||
const IPLocalhost = `((127\.([0-9]{1,3}.){2}[0-9]{1,3})|(::1))`
|
||||
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1))`
|
||||
|
||||
var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
|
||||
|
||||
|
|
125
vendor/src/github.com/docker/libnetwork/sandbox.go
vendored
125
vendor/src/github.com/docker/libnetwork/sandbox.go
vendored
|
@ -28,7 +28,7 @@ type Sandbox interface {
|
|||
// Labels returns the sandbox's labels
|
||||
Labels() map[string]interface{}
|
||||
// Statistics retrieves the interfaces' statistics for the sandbox
|
||||
Statistics() (map[string]*osl.InterfaceStatistics, error)
|
||||
Statistics() (map[string]*types.InterfaceStatistics, error)
|
||||
// Refresh leaves all the endpoints, resets and re-apply the options,
|
||||
// re-joins all the endpoints without destroying the osl sandbox
|
||||
Refresh(options ...SandboxOption) error
|
||||
|
@ -64,6 +64,8 @@ type sandbox struct {
|
|||
endpoints epHeap
|
||||
epPriority map[string]int
|
||||
joinLeaveDone chan struct{}
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -126,8 +128,8 @@ func (sb *sandbox) Labels() map[string]interface{} {
|
|||
return sb.config.generic
|
||||
}
|
||||
|
||||
func (sb *sandbox) Statistics() (map[string]*osl.InterfaceStatistics, error) {
|
||||
m := make(map[string]*osl.InterfaceStatistics)
|
||||
func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) {
|
||||
m := make(map[string]*types.InterfaceStatistics)
|
||||
|
||||
if sb.osSbox == nil {
|
||||
return m, nil
|
||||
|
@ -153,15 +155,24 @@ func (sb *sandbox) Delete() error {
|
|||
if ep.endpointInGWNetwork() {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := ep.Leave(sb); err != nil {
|
||||
log.Warnf("Failed detaching sandbox %s from endpoint %s: %v\n", sb.ID(), ep.ID(), err)
|
||||
}
|
||||
|
||||
if err := ep.Delete(); err != nil {
|
||||
log.Warnf("Failed deleting endpoint %s: %v\n", ep.ID(), err)
|
||||
}
|
||||
}
|
||||
|
||||
if sb.osSbox != nil {
|
||||
sb.osSbox.Destroy()
|
||||
}
|
||||
|
||||
if err := sb.storeDelete(); err != nil {
|
||||
log.Warnf("Failed to delete sandbox %s from store: %v", sb.ID(), err)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
delete(c.sandboxes, sb.ID())
|
||||
c.Unlock()
|
||||
|
@ -247,6 +258,19 @@ func (sb *sandbox) getConnectedEndpoints() []*endpoint {
|
|||
return eps
|
||||
}
|
||||
|
||||
func (sb *sandbox) getEndpoint(id string) *endpoint {
|
||||
sb.Lock()
|
||||
defer sb.Unlock()
|
||||
|
||||
for _, ep := range sb.endpoints {
|
||||
if ep.id == id {
|
||||
return ep
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateGateway(ep *endpoint) error {
|
||||
sb.Lock()
|
||||
osSbox := sb.osSbox
|
||||
|
@ -283,15 +307,21 @@ func (sb *sandbox) SetKey(basePath string) error {
|
|||
}
|
||||
|
||||
sb.Lock()
|
||||
if sb.osSbox != nil {
|
||||
osSbox := sb.osSbox
|
||||
sb.Unlock()
|
||||
return types.ForbiddenErrorf("failed to set sandbox key : already assigned")
|
||||
|
||||
if osSbox != nil {
|
||||
// If we already have an OS sandbox, release the network resources from that
|
||||
// and destroy the OS snab. We are moving into a new home further down. Note that none
|
||||
// of the network resources gets destroyed during the move.
|
||||
sb.releaseOSSbox()
|
||||
}
|
||||
sb.Unlock()
|
||||
osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key())
|
||||
|
||||
osSbox, err = osl.GetSandboxForExternalKey(basePath, sb.Key())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sb.Lock()
|
||||
sb.osSbox = osSbox
|
||||
sb.Unlock()
|
||||
|
@ -311,6 +341,45 @@ func (sb *sandbox) SetKey(basePath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func releaseOSSboxResources(osSbox osl.Sandbox, ep *endpoint) {
|
||||
for _, i := range osSbox.Info().Interfaces() {
|
||||
// Only remove the interfaces owned by this endpoint from the sandbox.
|
||||
if ep.hasInterface(i.SrcName()) {
|
||||
if err := i.Remove(); err != nil {
|
||||
log.Debugf("Remove interface failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ep.Lock()
|
||||
joinInfo := ep.joinInfo
|
||||
ep.Unlock()
|
||||
|
||||
// Remove non-interface routes.
|
||||
for _, r := range joinInfo.StaticRoutes {
|
||||
if err := osSbox.RemoveStaticRoute(r); err != nil {
|
||||
log.Debugf("Remove route failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *sandbox) releaseOSSbox() {
|
||||
sb.Lock()
|
||||
osSbox := sb.osSbox
|
||||
sb.osSbox = nil
|
||||
sb.Unlock()
|
||||
|
||||
if osSbox == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ep := range sb.getConnectedEndpoints() {
|
||||
releaseOSSboxResources(osSbox, ep)
|
||||
}
|
||||
|
||||
osSbox.Destroy()
|
||||
}
|
||||
|
||||
func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
||||
sb.Lock()
|
||||
if sb.osSbox == nil {
|
||||
|
@ -324,12 +393,12 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
|||
i := ep.iface
|
||||
ep.Unlock()
|
||||
|
||||
if i != nil {
|
||||
if i.srcName != "" {
|
||||
var ifaceOptions []osl.IfaceOption
|
||||
|
||||
ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(&i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))
|
||||
if i.addrv6.IP.To16() != nil {
|
||||
ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(&i.addrv6))
|
||||
ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().Address(i.addr), sb.osSbox.InterfaceOptions().Routes(i.routes))
|
||||
if i.addrv6 != nil && i.addrv6.IP.To16() != nil {
|
||||
ifaceOptions = append(ifaceOptions, sb.osSbox.InterfaceOptions().AddressIPv6(i.addrv6))
|
||||
}
|
||||
|
||||
if err := sb.osSbox.AddInterface(i.srcName, i.dstPrefix, ifaceOptions...); err != nil {
|
||||
|
@ -356,33 +425,21 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return sb.storeUpdate()
|
||||
}
|
||||
|
||||
func (sb *sandbox) clearNetworkResources(ep *endpoint) error {
|
||||
func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
||||
ep := sb.getEndpoint(origEp.id)
|
||||
if ep == nil {
|
||||
return fmt.Errorf("could not find the sandbox endpoint data for endpoint %s",
|
||||
ep.name)
|
||||
}
|
||||
|
||||
sb.Lock()
|
||||
osSbox := sb.osSbox
|
||||
sb.Unlock()
|
||||
if osSbox != nil {
|
||||
for _, i := range osSbox.Info().Interfaces() {
|
||||
// Only remove the interfaces owned by this endpoint from the sandbox.
|
||||
if ep.hasInterface(i.SrcName()) {
|
||||
if err := i.Remove(); err != nil {
|
||||
log.Debugf("Remove interface failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ep.Lock()
|
||||
joinInfo := ep.joinInfo
|
||||
ep.Unlock()
|
||||
|
||||
// Remove non-interface routes.
|
||||
for _, r := range joinInfo.StaticRoutes {
|
||||
if err := osSbox.RemoveStaticRoute(r); err != nil {
|
||||
log.Debugf("Remove route failed: %v", err)
|
||||
}
|
||||
}
|
||||
releaseOSSboxResources(osSbox, ep)
|
||||
}
|
||||
|
||||
sb.Lock()
|
||||
|
@ -423,7 +480,7 @@ func (sb *sandbox) clearNetworkResources(ep *endpoint) error {
|
|||
sb.updateGateway(gwepAfter)
|
||||
}
|
||||
|
||||
return nil
|
||||
return sb.storeUpdate()
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -837,7 +894,7 @@ func (eh epHeap) Less(i, j int) bool {
|
|||
cjp = 0
|
||||
}
|
||||
if cip == cjp {
|
||||
return eh[i].getNetwork().Name() < eh[j].getNetwork().Name()
|
||||
return eh[i].network.Name() < eh[j].network.Name()
|
||||
}
|
||||
|
||||
return cip > cjp
|
||||
|
|
211
vendor/src/github.com/docker/libnetwork/sandbox_store.go
vendored
Normal file
211
vendor/src/github.com/docker/libnetwork/sandbox_store.go
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
)
|
||||
|
||||
const (
|
||||
sandboxPrefix = "sandbox"
|
||||
)
|
||||
|
||||
type epState struct {
|
||||
Eid string
|
||||
Nid string
|
||||
}
|
||||
|
||||
type sbState struct {
|
||||
ID string
|
||||
Cid string
|
||||
c *controller
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
Eps []epState
|
||||
}
|
||||
|
||||
func (sbs *sbState) Key() []string {
|
||||
return []string{sandboxPrefix, sbs.ID}
|
||||
}
|
||||
|
||||
func (sbs *sbState) KeyPrefix() []string {
|
||||
return []string{sandboxPrefix}
|
||||
}
|
||||
|
||||
func (sbs *sbState) Value() []byte {
|
||||
b, err := json.Marshal(sbs)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (sbs *sbState) SetValue(value []byte) error {
|
||||
return json.Unmarshal(value, sbs)
|
||||
}
|
||||
|
||||
func (sbs *sbState) Index() uint64 {
|
||||
sbi, err := sbs.c.SandboxByID(sbs.ID)
|
||||
if err != nil {
|
||||
return sbs.dbIndex
|
||||
}
|
||||
|
||||
sb := sbi.(*sandbox)
|
||||
maxIndex := sb.dbIndex
|
||||
if sbs.dbIndex > maxIndex {
|
||||
maxIndex = sbs.dbIndex
|
||||
}
|
||||
|
||||
return maxIndex
|
||||
}
|
||||
|
||||
func (sbs *sbState) SetIndex(index uint64) {
|
||||
sbs.dbIndex = index
|
||||
sbs.dbExists = true
|
||||
|
||||
sbi, err := sbs.c.SandboxByID(sbs.ID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sb := sbi.(*sandbox)
|
||||
sb.dbIndex = index
|
||||
sb.dbExists = true
|
||||
}
|
||||
|
||||
func (sbs *sbState) Exists() bool {
|
||||
if sbs.dbExists {
|
||||
return sbs.dbExists
|
||||
}
|
||||
|
||||
sbi, err := sbs.c.SandboxByID(sbs.ID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
sb := sbi.(*sandbox)
|
||||
return sb.dbExists
|
||||
}
|
||||
|
||||
func (sbs *sbState) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (sbs *sbState) New() datastore.KVObject {
|
||||
return &sbState{c: sbs.c}
|
||||
}
|
||||
|
||||
func (sbs *sbState) CopyTo(o datastore.KVObject) error {
|
||||
dstSbs := o.(*sbState)
|
||||
dstSbs.c = sbs.c
|
||||
dstSbs.ID = sbs.ID
|
||||
dstSbs.Cid = sbs.Cid
|
||||
dstSbs.dbIndex = sbs.dbIndex
|
||||
dstSbs.dbExists = sbs.dbExists
|
||||
|
||||
for _, eps := range sbs.Eps {
|
||||
dstSbs.Eps = append(dstSbs.Eps, eps)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sbs *sbState) DataScope() string {
|
||||
return datastore.LocalScope
|
||||
}
|
||||
|
||||
func (sb *sandbox) storeUpdate() error {
|
||||
sbs := &sbState{
|
||||
c: sb.controller,
|
||||
ID: sb.id,
|
||||
}
|
||||
|
||||
for _, ep := range sb.getConnectedEndpoints() {
|
||||
eps := epState{
|
||||
Nid: ep.getNetwork().ID(),
|
||||
Eid: ep.ID(),
|
||||
}
|
||||
|
||||
sbs.Eps = append(sbs.Eps, eps)
|
||||
}
|
||||
|
||||
return sb.controller.updateToStore(sbs)
|
||||
}
|
||||
|
||||
func (sb *sandbox) storeDelete() error {
|
||||
sbs := &sbState{
|
||||
c: sb.controller,
|
||||
ID: sb.id,
|
||||
Cid: sb.containerID,
|
||||
dbIndex: sb.dbIndex,
|
||||
dbExists: sb.dbExists,
|
||||
}
|
||||
|
||||
return sb.controller.deleteFromStore(sbs)
|
||||
}
|
||||
|
||||
func (c *controller) sandboxCleanup() {
|
||||
store := c.getStore(datastore.LocalScope)
|
||||
if store == nil {
|
||||
logrus.Errorf("Could not find local scope store while trying to cleanup sandboxes")
|
||||
return
|
||||
}
|
||||
|
||||
kvol, err := store.List(datastore.Key(sandboxPrefix), &sbState{c: c})
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
logrus.Errorf("failed to get sandboxes for scope %s: %v", store.Scope(), err)
|
||||
return
|
||||
}
|
||||
|
||||
// It's normal for no sandboxes to be found. Just bail out.
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
return
|
||||
}
|
||||
|
||||
for _, kvo := range kvol {
|
||||
sbs := kvo.(*sbState)
|
||||
|
||||
sb := &sandbox{
|
||||
id: sbs.ID,
|
||||
controller: sbs.c,
|
||||
containerID: sbs.Cid,
|
||||
endpoints: epHeap{},
|
||||
epPriority: map[string]int{},
|
||||
dbIndex: sbs.dbIndex,
|
||||
dbExists: true,
|
||||
}
|
||||
|
||||
sb.osSbox, err = osl.NewSandbox(sb.Key(), true)
|
||||
if err != nil {
|
||||
logrus.Errorf("failed to create new osl sandbox while trying to build sandbox for cleanup: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, eps := range sbs.Eps {
|
||||
n, err := c.getNetworkFromStore(eps.Nid)
|
||||
if err != nil {
|
||||
logrus.Errorf("getNetworkFromStore for nid %s failed while trying to build sandbox for cleanup: %v", eps.Nid, err)
|
||||
continue
|
||||
}
|
||||
|
||||
ep, err := n.getEndpointFromStore(eps.Eid)
|
||||
if err != nil {
|
||||
logrus.Errorf("getEndpointFromStore for eid %s failed while trying to build sandbox for cleanup: %v", eps.Eid, err)
|
||||
continue
|
||||
}
|
||||
|
||||
heap.Push(&sb.endpoints, ep)
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.sandboxes[sb.id] = sb
|
||||
c.Unlock()
|
||||
|
||||
if err := sb.Delete(); err != nil {
|
||||
logrus.Errorf("failed to delete sandbox %s while trying to cleanup: %v", sb.id, err)
|
||||
}
|
||||
}
|
||||
}
|
626
vendor/src/github.com/docker/libnetwork/store.go
vendored
626
vendor/src/github.com/docker/libnetwork/store.go
vendored
|
@ -1,392 +1,366 @@
|
|||
package libnetwork
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultBoltTimeout = 3 * time.Second
|
||||
defaultLocalStoreConfig = config.DatastoreCfg{
|
||||
Embedded: true,
|
||||
Client: config.DatastoreClientCfg{
|
||||
Provider: "boltdb",
|
||||
Address: defaultPrefix + "/boltdb.db",
|
||||
Config: &store.Config{
|
||||
Bucket: "libnetwork",
|
||||
ConnectionTimeout: defaultBoltTimeout,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (c *controller) validateGlobalStoreConfig() bool {
|
||||
return c.cfg != nil && c.cfg.GlobalStore.Client.Provider != "" && c.cfg.GlobalStore.Client.Address != ""
|
||||
}
|
||||
|
||||
func (c *controller) initGlobalStore() error {
|
||||
func (c *controller) initStores() error {
|
||||
c.Lock()
|
||||
cfg := c.cfg
|
||||
if c.cfg == nil {
|
||||
c.Unlock()
|
||||
if !c.validateGlobalStoreConfig() {
|
||||
return fmt.Errorf("globalstore initialization requires a valid configuration")
|
||||
return nil
|
||||
}
|
||||
scopeConfigs := c.cfg.Scopes
|
||||
c.Unlock()
|
||||
|
||||
store, err := datastore.NewDataStore(&cfg.GlobalStore)
|
||||
for scope, scfg := range scopeConfigs {
|
||||
store, err := datastore.NewDataStore(scope, scfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
c.globalStore = store
|
||||
c.stores = append(c.stores, store)
|
||||
c.Unlock()
|
||||
|
||||
nws, err := c.getNetworksFromStore(true)
|
||||
if err == nil {
|
||||
c.processNetworkUpdate(nws, nil)
|
||||
} else if err != datastore.ErrKeyNotFound {
|
||||
log.Warnf("failed to read networks from globalstore during init : %v", err)
|
||||
}
|
||||
return c.watchNetworks()
|
||||
}
|
||||
|
||||
func (c *controller) initLocalStore() error {
|
||||
c.Lock()
|
||||
cfg := c.cfg
|
||||
c.Unlock()
|
||||
localStore, err := datastore.NewDataStore(c.getLocalStoreConfig(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
c.localStore = localStore
|
||||
c.Unlock()
|
||||
|
||||
nws, err := c.getNetworksFromStore(false)
|
||||
if err == nil {
|
||||
c.processNetworkUpdate(nws, nil)
|
||||
} else if err != datastore.ErrKeyNotFound {
|
||||
log.Warnf("failed to read networks from localstore during init : %v", err)
|
||||
}
|
||||
c.startWatch()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) getNetworksFromStore(global bool) ([]*store.KVPair, error) {
|
||||
var cs datastore.DataStore
|
||||
c.Lock()
|
||||
if global {
|
||||
cs = c.globalStore
|
||||
} else {
|
||||
cs = c.localStore
|
||||
func (c *controller) closeStores() {
|
||||
for _, store := range c.getStores() {
|
||||
store.Close()
|
||||
}
|
||||
c.Unlock()
|
||||
return cs.KVStore().List(datastore.Key(datastore.NetworkKeyPrefix))
|
||||
}
|
||||
|
||||
func (c *controller) newNetworkFromStore(n *network) error {
|
||||
n.Lock()
|
||||
func (c *controller) getStore(scope string) datastore.DataStore {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
for _, store := range c.stores {
|
||||
if store.Scope() == scope {
|
||||
return store
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) getStores() []datastore.DataStore {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
return c.stores
|
||||
}
|
||||
|
||||
func (c *controller) getNetworkFromStore(nid string) (*network, error) {
|
||||
for _, store := range c.getStores() {
|
||||
n := &network{id: nid, ctrlr: c}
|
||||
err := store.GetObject(datastore.Key(n.Key()...), n)
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return nil, fmt.Errorf("could not find network %s: %v", nid, err)
|
||||
}
|
||||
|
||||
// Continue searching in the next store if the key is not found in this store
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
continue
|
||||
}
|
||||
|
||||
ec := &endpointCnt{n: n}
|
||||
err = store.GetObject(datastore.Key(ec.Key()...), ec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not find endpoint count for network %s: %v", n.Name(), err)
|
||||
}
|
||||
|
||||
n.epCnt = ec
|
||||
return n, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("network %s not found", nid)
|
||||
}
|
||||
|
||||
func (c *controller) getNetworksFromStore() ([]*network, error) {
|
||||
var nl []*network
|
||||
|
||||
for _, store := range c.getStores() {
|
||||
kvol, err := store.List(datastore.Key(datastore.NetworkKeyPrefix),
|
||||
&network{ctrlr: c})
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return nil, fmt.Errorf("failed to get networks for scope %s: %v",
|
||||
store.Scope(), err)
|
||||
}
|
||||
|
||||
// Continue searching in the next store if no keys found in this store
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, kvo := range kvol {
|
||||
n := kvo.(*network)
|
||||
n.ctrlr = c
|
||||
n.endpoints = endpointTable{}
|
||||
n.Unlock()
|
||||
|
||||
return c.addNetwork(n)
|
||||
}
|
||||
|
||||
func (c *controller) newEndpointFromStore(key string, ep *endpoint) error {
|
||||
ep.Lock()
|
||||
n := ep.network
|
||||
id := ep.id
|
||||
ep.Unlock()
|
||||
|
||||
_, err := n.EndpointByID(id)
|
||||
ec := &endpointCnt{n: n}
|
||||
err = store.GetObject(datastore.Key(ec.Key()...), ec)
|
||||
if err != nil {
|
||||
if _, ok := err.(ErrNoSuchEndpoint); ok {
|
||||
return n.addEndpoint(ep)
|
||||
return nil, fmt.Errorf("could not find endpoint count key %s for network %s while listing: %v", datastore.Key(ec.Key()...), n.Name(), err)
|
||||
}
|
||||
|
||||
n.epCnt = ec
|
||||
nl = append(nl, n)
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
||||
return nl, nil
|
||||
}
|
||||
|
||||
func (c *controller) updateToStore(kvObject datastore.KV) error {
|
||||
if kvObject.Skip() {
|
||||
return nil
|
||||
}
|
||||
cs := c.getDataStore(kvObject.DataScope())
|
||||
if cs == nil {
|
||||
log.Debugf("datastore not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
func (n *network) getEndpointFromStore(eid string) (*endpoint, error) {
|
||||
for _, store := range n.ctrlr.getStores() {
|
||||
ep := &endpoint{id: eid, network: n}
|
||||
err := store.GetObject(datastore.Key(ep.Key()...), ep)
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return nil, fmt.Errorf("could not find endpoint %s: %v", eid, err)
|
||||
}
|
||||
|
||||
return cs.PutObjectAtomic(kvObject)
|
||||
}
|
||||
|
||||
func (c *controller) deleteFromStore(kvObject datastore.KV) error {
|
||||
if kvObject.Skip() {
|
||||
return nil
|
||||
}
|
||||
cs := c.getDataStore(kvObject.DataScope())
|
||||
if cs == nil {
|
||||
log.Debugf("datastore not initialized. kv object %s is not deleted from datastore", datastore.Key(kvObject.Key()...))
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := cs.DeleteObjectAtomic(kvObject); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) watchNetworks() error {
|
||||
if !c.validateGlobalStoreConfig() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
cs := c.globalStore
|
||||
c.Unlock()
|
||||
|
||||
networkKey := datastore.Key(datastore.NetworkKeyPrefix)
|
||||
if err := ensureKeys(networkKey, cs); err != nil {
|
||||
return fmt.Errorf("failed to ensure if the network keys are valid and present in store: %v", err)
|
||||
}
|
||||
nwPairs, err := cs.KVStore().WatchTree(networkKey, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case nws := <-nwPairs:
|
||||
c.Lock()
|
||||
tmpview := networkTable{}
|
||||
lview := c.networks
|
||||
c.Unlock()
|
||||
for k, v := range lview {
|
||||
if v.isGlobalScoped() {
|
||||
tmpview[k] = v
|
||||
}
|
||||
}
|
||||
c.processNetworkUpdate(nws, &tmpview)
|
||||
|
||||
// Delete processing
|
||||
for k := range tmpview {
|
||||
c.Lock()
|
||||
existing, ok := c.networks[k]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
// Continue searching in the next store if the key is not found in this store
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
continue
|
||||
}
|
||||
tmp := network{}
|
||||
if err := c.globalStore.GetObject(datastore.Key(existing.Key()...), &tmp); err != datastore.ErrKeyNotFound {
|
||||
continue
|
||||
|
||||
return ep, nil
|
||||
}
|
||||
if err := existing.deleteNetwork(); err != nil {
|
||||
log.Debugf("Delete failed %s: %s", existing.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
|
||||
return nil, fmt.Errorf("endpoint %s not found", eid)
|
||||
}
|
||||
|
||||
func (n *network) watchEndpoints() error {
|
||||
if n.Skip() || !n.ctrlr.validateGlobalStoreConfig() {
|
||||
return nil
|
||||
}
|
||||
func (n *network) getEndpointsFromStore() ([]*endpoint, error) {
|
||||
var epl []*endpoint
|
||||
|
||||
n.Lock()
|
||||
cs := n.ctrlr.globalStore
|
||||
tmp := endpoint{network: n}
|
||||
n.stopWatchCh = make(chan struct{})
|
||||
stopCh := n.stopWatchCh
|
||||
n.Unlock()
|
||||
|
||||
endpointKey := datastore.Key(tmp.KeyPrefix()...)
|
||||
if err := ensureKeys(endpointKey, cs); err != nil {
|
||||
return fmt.Errorf("failed to ensure if the endpoint keys are valid and present in store: %v", err)
|
||||
for _, store := range n.getController().getStores() {
|
||||
kvol, err := store.List(datastore.Key(tmp.KeyPrefix()...), &endpoint{network: n})
|
||||
if err != nil && err != datastore.ErrKeyNotFound {
|
||||
return nil,
|
||||
fmt.Errorf("failed to get endpoints for network %s scope %s: %v",
|
||||
n.Name(), store.Scope(), err)
|
||||
}
|
||||
epPairs, err := cs.KVStore().WatchTree(endpointKey, stopCh)
|
||||
if err != nil {
|
||||
|
||||
// Continue searching in the next store if no keys found in this store
|
||||
if err == datastore.ErrKeyNotFound {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, kvo := range kvol {
|
||||
ep := kvo.(*endpoint)
|
||||
ep.network = n
|
||||
epl = append(epl, ep)
|
||||
}
|
||||
}
|
||||
|
||||
return epl, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if err := cs.PutObjectAtomic(kvObject); err != nil {
|
||||
if err == datastore.ErrKeyModified {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
return fmt.Errorf("failed to update store for object type %T: %v", kvObject, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
retry:
|
||||
if err := cs.DeleteObjectAtomic(kvObject); err != nil {
|
||||
if err == datastore.ErrKeyModified {
|
||||
if err := cs.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil {
|
||||
return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err)
|
||||
}
|
||||
goto retry
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type netWatch struct {
|
||||
localEps map[string]*endpoint
|
||||
remoteEps map[string]*endpoint
|
||||
stopCh chan struct{}
|
||||
}
|
||||
|
||||
func (c *controller) getLocalEps(nw *netWatch) []*endpoint {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
|
||||
var epl []*endpoint
|
||||
for _, ep := range nw.localEps {
|
||||
epl = append(epl, ep)
|
||||
}
|
||||
|
||||
return epl
|
||||
}
|
||||
|
||||
func (c *controller) watchSvcRecord(ep *endpoint) {
|
||||
c.watchCh <- ep
|
||||
}
|
||||
|
||||
func (c *controller) unWatchSvcRecord(ep *endpoint) {
|
||||
c.unWatchCh <- ep
|
||||
}
|
||||
|
||||
func (c *controller) networkWatchLoop(nw *netWatch, ep *endpoint, ecCh <-chan datastore.KVObject) {
|
||||
for {
|
||||
select {
|
||||
case <-stopCh:
|
||||
case <-nw.stopCh:
|
||||
return
|
||||
case eps := <-epPairs:
|
||||
n.Lock()
|
||||
tmpview := endpointTable{}
|
||||
lview := n.endpoints
|
||||
n.Unlock()
|
||||
for k, v := range lview {
|
||||
if v.network.isGlobalScoped() {
|
||||
tmpview[k] = v
|
||||
}
|
||||
}
|
||||
n.ctrlr.processEndpointsUpdate(eps, &tmpview)
|
||||
// Delete processing
|
||||
for k := range tmpview {
|
||||
n.Lock()
|
||||
existing, ok := n.endpoints[k]
|
||||
n.Unlock()
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
tmp := endpoint{}
|
||||
if err := cs.GetObject(datastore.Key(existing.Key()...), &tmp); err != datastore.ErrKeyNotFound {
|
||||
continue
|
||||
}
|
||||
if err := existing.deleteEndpoint(); err != nil {
|
||||
log.Debugf("Delete failed %s: %s", existing.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
case o := <-ecCh:
|
||||
ec := o.(*endpointCnt)
|
||||
|
||||
func (n *network) stopWatch() {
|
||||
n.Lock()
|
||||
if n.stopWatchCh != nil {
|
||||
close(n.stopWatchCh)
|
||||
n.stopWatchCh = nil
|
||||
}
|
||||
n.Unlock()
|
||||
}
|
||||
|
||||
func (c *controller) processNetworkUpdate(nws []*store.KVPair, prune *networkTable) {
|
||||
for _, kve := range nws {
|
||||
var n network
|
||||
err := json.Unmarshal(kve.Value, &n)
|
||||
epl, err := ec.n.getEndpointsFromStore()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
break
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
var addEp []*endpoint
|
||||
|
||||
delEpMap := make(map[string]*endpoint)
|
||||
for k, v := range nw.remoteEps {
|
||||
delEpMap[k] = v
|
||||
}
|
||||
|
||||
for _, lEp := range epl {
|
||||
if _, ok := nw.localEps[lEp.ID()]; ok {
|
||||
continue
|
||||
}
|
||||
if prune != nil {
|
||||
delete(*prune, n.id)
|
||||
|
||||
if _, ok := nw.remoteEps[lEp.ID()]; ok {
|
||||
delete(delEpMap, lEp.ID())
|
||||
continue
|
||||
}
|
||||
|
||||
nw.remoteEps[lEp.ID()] = lEp
|
||||
addEp = append(addEp, lEp)
|
||||
|
||||
}
|
||||
n.SetIndex(kve.LastIndex)
|
||||
c.Lock()
|
||||
existing, ok := c.networks[n.id]
|
||||
c.Unlock()
|
||||
|
||||
for _, lEp := range addEp {
|
||||
ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), true)
|
||||
}
|
||||
|
||||
for _, lEp := range delEpMap {
|
||||
ep.getNetwork().updateSvcRecord(lEp, c.getLocalEps(nw), false)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoint) {
|
||||
c.Lock()
|
||||
nw, ok := nmap[ep.getNetwork().ID()]
|
||||
c.Unlock()
|
||||
|
||||
if ok {
|
||||
existing.Lock()
|
||||
// Skip existing network update
|
||||
if existing.dbIndex != n.Index() {
|
||||
// Can't use SetIndex() since existing is locked.
|
||||
existing.dbIndex = n.Index()
|
||||
existing.dbExists = true
|
||||
existing.endpointCnt = n.endpointCnt
|
||||
}
|
||||
existing.Unlock()
|
||||
continue
|
||||
}
|
||||
|
||||
if err = c.newNetworkFromStore(&n); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) processEndpointUpdate(ep *endpoint) bool {
|
||||
nw := ep.network
|
||||
if nw == nil {
|
||||
return true
|
||||
}
|
||||
nw.Lock()
|
||||
id := nw.id
|
||||
nw.Unlock()
|
||||
// Update the svc db for the local endpoint join right away
|
||||
ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), true)
|
||||
|
||||
c.Lock()
|
||||
n, ok := c.networks[id]
|
||||
c.Unlock()
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
existing, _ := n.EndpointByID(ep.id)
|
||||
if existing == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
ee := existing.(*endpoint)
|
||||
ee.Lock()
|
||||
if ee.dbIndex != ep.Index() {
|
||||
// Can't use SetIndex() because ee is locked.
|
||||
ee.dbIndex = ep.Index()
|
||||
ee.dbExists = true
|
||||
ee.sandboxID = ep.sandboxID
|
||||
}
|
||||
ee.Unlock()
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func ensureKeys(key string, cs datastore.DataStore) error {
|
||||
exists, err := cs.KVStore().Exists(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exists {
|
||||
return nil
|
||||
}
|
||||
return cs.KVStore().Put(key, []byte{}, nil)
|
||||
}
|
||||
|
||||
func (c *controller) getLocalStoreConfig(cfg *config.Config) *config.DatastoreCfg {
|
||||
if cfg != nil && cfg.LocalStore.Client.Provider != "" && cfg.LocalStore.Client.Address != "" {
|
||||
return &cfg.LocalStore
|
||||
}
|
||||
return &defaultLocalStoreConfig
|
||||
}
|
||||
|
||||
func (c *controller) getDataStore(dataScope datastore.DataScope) (dataStore datastore.DataStore) {
|
||||
c.Lock()
|
||||
if dataScope == datastore.GlobalScope {
|
||||
dataStore = c.globalStore
|
||||
} else if dataScope == datastore.LocalScope {
|
||||
dataStore = c.localStore
|
||||
}
|
||||
nw.localEps[ep.ID()] = ep
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
nw = &netWatch{
|
||||
localEps: make(map[string]*endpoint),
|
||||
remoteEps: make(map[string]*endpoint),
|
||||
}
|
||||
|
||||
// Update the svc db for the local endpoint join right away
|
||||
// Do this before adding this ep to localEps so that we don't
|
||||
// try to update this ep's container's svc records
|
||||
ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), true)
|
||||
|
||||
c.Lock()
|
||||
nw.localEps[ep.ID()] = ep
|
||||
nmap[ep.getNetwork().ID()] = nw
|
||||
nw.stopCh = make(chan struct{})
|
||||
c.Unlock()
|
||||
|
||||
store := c.getStore(ep.getNetwork().DataScope())
|
||||
if store == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !store.Watchable() {
|
||||
return
|
||||
}
|
||||
|
||||
ch, err := store.Watch(ep.getNetwork().getEpCnt(), nw.stopCh)
|
||||
if err != nil {
|
||||
log.Warnf("Error creating watch for network: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
go c.networkWatchLoop(nw, ep, ch)
|
||||
}
|
||||
|
||||
func (c *controller) processEndpointsUpdate(eps []*store.KVPair, prune *endpointTable) {
|
||||
for _, epe := range eps {
|
||||
var ep endpoint
|
||||
err := json.Unmarshal(epe.Value, &ep)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
if prune != nil {
|
||||
delete(*prune, ep.id)
|
||||
}
|
||||
ep.SetIndex(epe.LastIndex)
|
||||
if nid, err := ep.networkIDFromKey(epe.Key); err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
} else {
|
||||
if n, err := c.NetworkByID(nid); err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
} else {
|
||||
ep.network = n.(*network)
|
||||
func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoint) {
|
||||
c.Lock()
|
||||
nw, ok := nmap[ep.getNetwork().ID()]
|
||||
|
||||
if ok {
|
||||
delete(nw.localEps, ep.ID())
|
||||
c.Unlock()
|
||||
|
||||
// Update the svc db about local endpoint leave right away
|
||||
// Do this after we remove this ep from localEps so that we
|
||||
// don't try to remove this svc record from this ep's container.
|
||||
ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), false)
|
||||
|
||||
c.Lock()
|
||||
if len(nw.localEps) == 0 {
|
||||
close(nw.stopCh)
|
||||
delete(nmap, ep.getNetwork().ID())
|
||||
}
|
||||
}
|
||||
if c.processEndpointUpdate(&ep) {
|
||||
err = c.newEndpointFromStore(epe.Key, &ep)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
func (c *controller) watchLoop(nmap map[string]*netWatch) {
|
||||
for {
|
||||
select {
|
||||
case ep := <-c.watchCh:
|
||||
c.processEndpointCreate(nmap, ep)
|
||||
case ep := <-c.unWatchCh:
|
||||
c.processEndpointDelete(nmap, ep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *controller) startWatch() {
|
||||
c.watchCh = make(chan *endpoint)
|
||||
c.unWatchCh = make(chan *endpoint)
|
||||
nmap := make(map[string]*netWatch)
|
||||
|
||||
go c.watchLoop(nmap)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -17,11 +18,46 @@ type TransportPort struct {
|
|||
Port uint16
|
||||
}
|
||||
|
||||
// Equal checks if this instance of Transportport is equal to the passed one
|
||||
func (t *TransportPort) Equal(o *TransportPort) bool {
|
||||
if t == o {
|
||||
return true
|
||||
}
|
||||
|
||||
if o == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if t.Proto != o.Proto || t.Port != o.Port {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// GetCopy returns a copy of this TransportPort structure instance
|
||||
func (t *TransportPort) GetCopy() TransportPort {
|
||||
return TransportPort{Proto: t.Proto, Port: t.Port}
|
||||
}
|
||||
|
||||
// String returns the TransportPort structure in string form
|
||||
func (t *TransportPort) String() string {
|
||||
return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port)
|
||||
}
|
||||
|
||||
// FromString reads the TransportPort structure from string
|
||||
func (t *TransportPort) FromString(s string) error {
|
||||
ps := strings.Split(s, "/")
|
||||
if len(ps) == 2 {
|
||||
t.Proto = ParseProtocol(ps[0])
|
||||
if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil {
|
||||
t.Port = uint16(p)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return BadRequestErrorf("invalid format for transport port: %s", s)
|
||||
}
|
||||
|
||||
// PortBinding represent a port binding between the container and the host
|
||||
type PortBinding struct {
|
||||
Proto Protocol
|
||||
|
@ -68,6 +104,62 @@ func (p *PortBinding) GetCopy() PortBinding {
|
|||
}
|
||||
}
|
||||
|
||||
// String return the PortBinding structure in string form
|
||||
func (p *PortBinding) String() string {
|
||||
ret := fmt.Sprintf("%s/", p.Proto)
|
||||
if p.IP != nil {
|
||||
ret = fmt.Sprintf("%s%s", ret, p.IP.String())
|
||||
}
|
||||
ret = fmt.Sprintf("%s:%d/", ret, p.Port)
|
||||
if p.HostIP != nil {
|
||||
ret = fmt.Sprintf("%s%s", ret, p.HostIP.String())
|
||||
}
|
||||
ret = fmt.Sprintf("%s:%d", ret, p.HostPort)
|
||||
return ret
|
||||
}
|
||||
|
||||
// FromString reads the TransportPort structure from string
|
||||
func (p *PortBinding) FromString(s string) error {
|
||||
ps := strings.Split(s, "/")
|
||||
if len(ps) != 3 {
|
||||
return BadRequestErrorf("invalid format for port binding: %s", s)
|
||||
}
|
||||
|
||||
p.Proto = ParseProtocol(ps[0])
|
||||
|
||||
var err error
|
||||
if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil {
|
||||
return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error())
|
||||
}
|
||||
|
||||
if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil {
|
||||
return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseIPPort(s string) (net.IP, uint16, error) {
|
||||
pp := strings.Split(s, ":")
|
||||
if len(pp) != 2 {
|
||||
return nil, 0, BadRequestErrorf("invalid format: %s", s)
|
||||
}
|
||||
|
||||
var ip net.IP
|
||||
if pp[0] != "" {
|
||||
if ip = net.ParseIP(pp[0]); ip == nil {
|
||||
return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
|
||||
}
|
||||
}
|
||||
|
||||
port, err := strconv.ParseUint(pp[1], 10, 16)
|
||||
if err != nil {
|
||||
return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
|
||||
}
|
||||
|
||||
return ip, uint16(port), nil
|
||||
}
|
||||
|
||||
// Equal checks if this instance of PortBinding is equal to the passed one
|
||||
func (p *PortBinding) Equal(o *PortBinding) bool {
|
||||
if p == o {
|
||||
|
@ -154,6 +246,9 @@ func ParseProtocol(s string) Protocol {
|
|||
|
||||
// GetMacCopy returns a copy of the passed MAC address
|
||||
func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
|
||||
if from == nil {
|
||||
return nil
|
||||
}
|
||||
to := make(net.HardwareAddr, len(from))
|
||||
copy(to, from)
|
||||
return to
|
||||
|
@ -161,6 +256,9 @@ func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
|
|||
|
||||
// GetIPCopy returns a copy of the passed IP address
|
||||
func GetIPCopy(from net.IP) net.IP {
|
||||
if from == nil {
|
||||
return nil
|
||||
}
|
||||
to := make(net.IP, len(from))
|
||||
copy(to, from)
|
||||
return to
|
||||
|
@ -222,23 +320,32 @@ func GetMinimalIPNet(nw *net.IPNet) *net.IPNet {
|
|||
|
||||
var v4inV6MaskPrefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
|
||||
// GetHostPartIP returns the host portion of the ip address identified by the mask.
|
||||
// IP address representation is not modified. If address and mask are not compatible
|
||||
// an error is returned.
|
||||
func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
||||
// compareIPMask checks if the passed ip and mask are semantically compatible.
|
||||
// It returns the byte indexes for the address and mask so that caller can
|
||||
// do bitwise operations without modifying address representation.
|
||||
func compareIPMask(ip net.IP, mask net.IPMask) (is int, ms int, err error) {
|
||||
// Find the effective starting of address and mask
|
||||
is := 0
|
||||
ms := 0
|
||||
if len(ip) == net.IPv6len && ip.To4() != nil {
|
||||
is = 12
|
||||
}
|
||||
if len(ip[is:]) == net.IPv4len && len(mask) == net.IPv6len && bytes.Equal(mask[:12], v4inV6MaskPrefix) {
|
||||
ms = 12
|
||||
}
|
||||
|
||||
// Check if address and mask are semantically compatible
|
||||
if len(ip[is:]) != len(mask[ms:]) {
|
||||
return nil, fmt.Errorf("cannot compute host portion ip address as ip and mask are not compatible: (%#v, %#v)", ip, mask)
|
||||
err = fmt.Errorf("ip and mask are not compatible: (%#v, %#v)", ip, mask)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetHostPartIP returns the host portion of the ip address identified by the mask.
|
||||
// IP address representation is not modified. If address and mask are not compatible
|
||||
// an error is returned.
|
||||
func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
||||
// Find the effective starting of address and mask
|
||||
is, ms, err := compareIPMask(ip, mask)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot compute host portion ip address because %s", err)
|
||||
}
|
||||
|
||||
// Compute host portion
|
||||
|
@ -250,6 +357,34 @@ func GetHostPartIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
// GetBroadcastIP returns the broadcast ip address for the passed network (ip and mask).
|
||||
// IP address representation is not modified. If address and mask are not compatible
|
||||
// an error is returned.
|
||||
func GetBroadcastIP(ip net.IP, mask net.IPMask) (net.IP, error) {
|
||||
// Find the effective starting of address and mask
|
||||
is, ms, err := compareIPMask(ip, mask)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot compute broadcast ip address because %s", err)
|
||||
}
|
||||
|
||||
// Compute broadcast address
|
||||
out := GetIPCopy(ip)
|
||||
for i := 0; i < len(mask[ms:]); i++ {
|
||||
out[is+i] |= ^mask[ms+i]
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ParseCIDR returns the *net.IPNet represented by the passed CIDR notation
|
||||
func ParseCIDR(cidr string) (n *net.IPNet, e error) {
|
||||
var i net.IP
|
||||
if i, n, e = net.ParseCIDR(cidr); e == nil {
|
||||
n.IP = i
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
// NEXTHOP indicates a StaticRoute with an IP next hop.
|
||||
NEXTHOP = iota
|
||||
|
@ -278,6 +413,23 @@ func (r *StaticRoute) GetCopy() *StaticRoute {
|
|||
}
|
||||
}
|
||||
|
||||
// InterfaceStatistics represents the interface's statistics
|
||||
type InterfaceStatistics struct {
|
||||
RxBytes uint64
|
||||
RxPackets uint64
|
||||
RxErrors uint64
|
||||
RxDropped uint64
|
||||
TxBytes uint64
|
||||
TxPackets uint64
|
||||
TxErrors uint64
|
||||
TxDropped uint64
|
||||
}
|
||||
|
||||
func (is *InterfaceStatistics) String() string {
|
||||
return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
|
||||
is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
|
||||
}
|
||||
|
||||
/******************************
|
||||
* Well-known Error Interfaces
|
||||
******************************/
|
||||
|
|
Loading…
Reference in a new issue