Merge pull request #37485 from fcrisciani/resolv

Systemd-resolved proper handling
This commit is contained in:
Sebastiaan van Stijn 2018-08-01 14:52:33 +02:00 committed by GitHub
commit e1584514c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 460 additions and 236 deletions

View file

@ -75,6 +75,8 @@ type commonBridgeConfig struct {
type NetworkConfig struct { type NetworkConfig struct {
// Default address pools for docker networks // Default address pools for docker networks
DefaultAddressPools opts.PoolsOpt `json:"default-address-pools,omitempty"` DefaultAddressPools opts.PoolsOpt `json:"default-address-pools,omitempty"`
// NetworkControlPlaneMTU allows to specify the control plane MTU, this will allow to optimize the network use in some components
NetworkControlPlaneMTU int `json:"network-control-plane-mtu,omitempty"`
} }
// CommonTLSOptions defines TLS configuration for the daemon server. // CommonTLSOptions defines TLS configuration for the daemon server.
@ -192,8 +194,6 @@ type CommonConfig struct {
// Exposed node Generic Resources // Exposed node Generic Resources
// e.g: ["orange=red", "orange=green", "orange=blue", "apple=3"] // e.g: ["orange=red", "orange=green", "orange=blue", "apple=3"]
NodeGenericResources []string `json:"node-generic-resources,omitempty"` NodeGenericResources []string `json:"node-generic-resources,omitempty"`
// NetworkControlPlaneMTU allows to specify the control plane MTU, this will allow to optimize the network use in some components
NetworkControlPlaneMTU int `json:"network-control-plane-mtu,omitempty"`
// ContainerAddr is the address used to connect to containerd if we're // ContainerAddr is the address used to connect to containerd if we're
// not starting it ourselves // not starting it ourselves

View file

@ -69,3 +69,9 @@ func (conf *Config) GetInitPath() string {
} }
return DefaultInitBinary return DefaultInitBinary
} }
// GetResolvConf returns the appropriate resolv.conf
// Check setupResolvConf on how this is selected
func (conf *Config) GetResolvConf() string {
return conf.ResolvConf
}

View file

@ -37,6 +37,8 @@ type Config struct {
ShmSize opts.MemBytes `json:"default-shm-size,omitempty"` ShmSize opts.MemBytes `json:"default-shm-size,omitempty"`
NoNewPrivileges bool `json:"no-new-privileges,omitempty"` NoNewPrivileges bool `json:"no-new-privileges,omitempty"`
IpcMode string `json:"default-ipc-mode,omitempty"` IpcMode string `json:"default-ipc-mode,omitempty"`
// ResolvConf is the path to the configuration of the host resolver
ResolvConf string `json:"resolv-conf,omitempty"`
} }
// BridgeConfig stores all the bridge driver specific // BridgeConfig stores all the bridge driver specific

View file

@ -63,21 +63,13 @@ func (daemon *Daemon) buildSandboxOptions(container *container.Container) ([]lib
if container.HostConfig.NetworkMode.IsHost() { if container.HostConfig.NetworkMode.IsHost() {
sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox())
if len(container.HostConfig.ExtraHosts) == 0 {
sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
}
if len(container.HostConfig.DNS) == 0 && len(daemon.configStore.DNS) == 0 &&
len(container.HostConfig.DNSSearch) == 0 && len(daemon.configStore.DNSSearch) == 0 &&
len(container.HostConfig.DNSOptions) == 0 && len(daemon.configStore.DNSOptions) == 0 {
sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
}
} else { } else {
// OptionUseExternalKey is mandatory for userns support. // OptionUseExternalKey is mandatory for userns support.
// But optional for non-userns support // But optional for non-userns support
sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey())
} }
if err = setupPathsAndSandboxOptions(container, &sboxOptions); err != nil { if err = daemon.setupPathsAndSandboxOptions(container, &sboxOptions); err != nil {
return nil, err return nil, err
} }

View file

@ -369,9 +369,17 @@ func (daemon *Daemon) isNetworkHotPluggable() bool {
return true return true
} }
func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
var err error var err error
if container.HostConfig.NetworkMode.IsHost() {
// Point to the host files, so that will be copied into the container running in host mode
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts"))
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf"))
} else {
*sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf()))
}
container.HostsPath, err = container.GetRootResourcePath("hosts") container.HostsPath, err = container.GetRootResourcePath("hosts")
if err != nil { if err != nil {
return err return err

View file

@ -155,7 +155,7 @@ func (daemon *Daemon) isNetworkHotPluggable() bool {
return true return true
} }
func setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error {
return nil return nil
} }

View file

@ -581,6 +581,9 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
// Do we have a disabled network? // Do we have a disabled network?
config.DisableBridge = isBridgeNetworkDisabled(config) config.DisableBridge = isBridgeNetworkDisabled(config)
// Setup the resolv.conf
setupResolvConf(config)
// Verify the platform is supported as a daemon // Verify the platform is supported as a daemon
if !platformSupported { if !platformSupported {
return nil, errSystemNotSupported return nil, errSystemNotSupported

View file

@ -8,12 +8,19 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/internal/procfs"
"github.com/docker/docker/pkg/fileutils" "github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const (
defaultResolvConf = "/etc/resolv.conf"
alternateResolvConf = "/run/systemd/resolve/resolv.conf"
)
// On Linux, plugins use a static path for storing execution state, // On Linux, plugins use a static path for storing execution state,
// instead of deriving path from daemon's exec-root. This is because // instead of deriving path from daemon's exec-root. This is because
// plugin socket files are created here and they cannot exceed max // plugin socket files are created here and they cannot exceed max
@ -131,3 +138,30 @@ func shouldUnmountRoot(root string, info *mount.Info) bool {
} }
return hasMountinfoOption(info.Optional, sharedPropagationOption) return hasMountinfoOption(info.Optional, sharedPropagationOption)
} }
// setupResolvConf sets the appropriate resolv.conf file if not specified
// When systemd-resolved is running the default /etc/resolv.conf points to
// localhost. In this case fetch the alternative config file that is in a
// different path so that containers can use it
// In all the other cases fallback to the default one
func setupResolvConf(config *config.Config) {
if config.ResolvConf != "" {
return
}
config.ResolvConf = defaultResolvConf
pids, err := procfs.PidOf("systemd-resolved")
if err != nil {
logrus.Errorf("unable to check systemd-resolved status: %s", err)
return
}
if len(pids) > 0 && pids[0] > 0 {
_, err := os.Stat(alternateResolvConf)
if err == nil {
logrus.Infof("systemd-resolved is running, so using resolvconf: %s", alternateResolvConf)
config.ResolvConf = alternateResolvConf
return
}
logrus.Infof("systemd-resolved is running, but %s is not present, fallback to %s", alternateResolvConf, defaultResolvConf)
}
}

View file

@ -1,5 +1,9 @@
// +build !linux,!freebsd,!windows // +build !linux,!freebsd,!windows
package daemon // import "github.com/docker/docker/daemon" package daemon // import "github.com/docker/docker/daemon"
import "github.com/docker/docker/daemon/config"
const platformSupported = false const platformSupported = false
func setupResolvConf(config *config.Config) {
}

View file

@ -653,3 +653,6 @@ func (daemon *Daemon) loadRuntimes() error {
func (daemon *Daemon) initRuntimes(_ map[string]types.Runtime) error { func (daemon *Daemon) initRuntimes(_ map[string]types.Runtime) error {
return nil return nil
} }
func setupResolvConf(config *config.Config) {
}

View file

@ -3,7 +3,7 @@
# LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When # LIBNETWORK_COMMIT is used to build the docker-userland-proxy binary. When
# updating the binary version, consider updating github.com/docker/libnetwork # updating the binary version, consider updating github.com/docker/libnetwork
# in vendor.conf accordingly # in vendor.conf accordingly
LIBNETWORK_COMMIT=3ac297bc7fd0afec9051bbb47024c9bc1d75bf5b LIBNETWORK_COMMIT=f30a35b091cc2a431ef9856c75c343f75bb5f2e2
install_proxy() { install_proxy() {
case "$1" in case "$1" in

View file

@ -0,0 +1,105 @@
package procfs
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"unicode"
"github.com/sirupsen/logrus"
)
// PidOf finds process(es) with a specified name (regexp match)
// and return their pid(s)
func PidOf(name string) ([]int, error) {
if len(name) == 0 {
return []int{}, fmt.Errorf("name should not be empty")
}
re, err := regexp.Compile("(^|/)" + name + "$")
if err != nil {
return []int{}, err
}
return getPids(re), nil
}
func getPids(re *regexp.Regexp) []int {
pids := []int{}
dirFD, err := os.Open("/proc")
if err != nil {
return nil
}
defer dirFD.Close()
for {
// Read a small number at a time in case there are many entries, we don't want to
// allocate a lot here.
ls, err := dirFD.Readdir(10)
if err == io.EOF {
break
}
if err != nil {
return nil
}
for _, entry := range ls {
if !entry.IsDir() {
continue
}
// If the directory is not a number (i.e. not a PID), skip it
pid, err := strconv.Atoi(entry.Name())
if err != nil {
continue
}
cmdline, err := ioutil.ReadFile(filepath.Join("/proc", entry.Name(), "cmdline"))
if err != nil {
logrus.Infof("Error reading file %s: %+v", filepath.Join("/proc", entry.Name(), "cmdline"), err)
continue
}
// The bytes we read have '\0' as a separator for the command line
parts := bytes.SplitN(cmdline, []byte{0}, 2)
if len(parts) == 0 {
continue
}
// Split the command line itself we are interested in just the first part
exe := strings.FieldsFunc(string(parts[0]), func(c rune) bool {
return unicode.IsSpace(c) || c == ':'
})
if len(exe) == 0 {
continue
}
// Check if the name of the executable is what we are looking for
if re.MatchString(exe[0]) {
// Grab the PID from the directory path
pids = append(pids, pid)
}
}
}
return pids
}

View file

@ -0,0 +1,36 @@
package procfs
import (
"os"
"path/filepath"
"regexp"
"runtime"
"testing"
"gotest.tools/assert"
)
func TestPidOf(t *testing.T) {
pids, err := PidOf(filepath.Base(os.Args[0]))
assert.NilError(t, err)
assert.Check(t, len(pids) == 1)
assert.DeepEqual(t, pids[0], os.Getpid())
}
func BenchmarkGetPids(b *testing.B) {
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
b.Skipf("not supported on GOOS=%s", runtime.GOOS)
}
re, err := regexp.Compile("(^|/)" + filepath.Base(os.Args[0]) + "$")
assert.Check(b, err == nil)
for i := 0; i < b.N; i++ {
pids := getPids(re)
b.StopTimer()
assert.Check(b, len(pids) > 0)
assert.Check(b, pids[0] == os.Getpid())
b.StartTimer()
}
}

View file

@ -37,7 +37,7 @@ github.com/mitchellh/hashstructure 2bca23e0e452137f789efbc8610126fd8b94f73b
#get libnetwork packages #get libnetwork packages
# When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
github.com/docker/libnetwork d00ceed44cc447c77f25cdf5d59e83163bdcb4c9 github.com/docker/libnetwork f30a35b091cc2a431ef9856c75c343f75bb5f2e2
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec

View file

@ -15,6 +15,17 @@ There are many networking solutions available to suit a broad range of use-cases
```go ```go
import (
"fmt"
"log"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
)
func main() { func main() {
if reexec.Init() { if reexec.Init() {
return return

View file

@ -194,7 +194,7 @@ func (c *controller) handleKeyChange(keys []*types.EncryptionKey) error {
func (c *controller) agentSetup(clusterProvider cluster.Provider) error { func (c *controller) agentSetup(clusterProvider cluster.Provider) error {
agent := c.getAgent() agent := c.getAgent()
// If the agent is already present there is no need to try to initilize it again // If the agent is already present there is no need to try to initialize it again
if agent != nil { if agent != nil {
return nil return nil
} }

View file

@ -372,7 +372,7 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool, serial
h.Lock() h.Lock()
} }
// Previous atomic push was succesfull. Save private copy to local copy // Previous atomic push was successful. Save private copy to local copy
h.unselected = nh.unselected h.unselected = nh.unselected
h.head = nh.head h.head = nh.head
h.dbExists = nh.dbExists h.dbExists = nh.dbExists

View file

@ -121,7 +121,7 @@ type NetworkController interface {
// Stop network controller // Stop network controller
Stop() Stop()
// ReloadCondfiguration updates the controller configuration // ReloadConfiguration updates the controller configuration
ReloadConfiguration(cfgOptions ...config.Option) error ReloadConfiguration(cfgOptions ...config.Option) error
// SetClusterProvider sets cluster provider // SetClusterProvider sets cluster provider
@ -1107,6 +1107,8 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S
sb.config.hostsPath = filepath.Join(c.cfg.Daemon.DataDir, "/network/files/hosts") sb.config.hostsPath = filepath.Join(c.cfg.Daemon.DataDir, "/network/files/hosts")
sb.config.resolvConfPath = filepath.Join(c.cfg.Daemon.DataDir, "/network/files/resolv.conf") sb.config.resolvConfPath = filepath.Join(c.cfg.Daemon.DataDir, "/network/files/resolv.conf")
sb.id = "ingress_sbox" sb.id = "ingress_sbox"
} else if sb.loadBalancerNID != "" {
sb.id = "lb_" + sb.loadBalancerNID
} }
c.Unlock() c.Unlock()

View file

@ -185,7 +185,7 @@ func Key(key ...string) string {
func ParseKey(key string) ([]string, error) { func ParseKey(key string) ([]string, error) {
chain := strings.Split(strings.Trim(key, "/"), "/") chain := strings.Split(strings.Trim(key, "/"), "/")
// The key must atleast be equal to the rootChain in order to be considered as valid // The key must at least be equal to the rootChain in order to be considered as valid
if len(chain) <= len(rootChain) || !reflect.DeepEqual(chain[0:len(rootChain)], rootChain) { if len(chain) <= len(rootChain) || !reflect.DeepEqual(chain[0:len(rootChain)], rootChain) {
return nil, types.BadRequestErrorf("invalid Key : %s", key) return nil, types.BadRequestErrorf("invalid Key : %s", key)
} }
@ -589,7 +589,7 @@ func (ds *datastore) DeleteObject(kvObject KVObject) error {
defer ds.Unlock() defer ds.Unlock()
} }
// cleaup the cache first // cleanup the cache first
if ds.cache != nil { if ds.cache != nil {
// If persistent store is skipped, sequencing needs to // If persistent store is skipped, sequencing needs to
// happen in cache. // happen in cache.
@ -645,7 +645,7 @@ func (ds *datastore) DeleteTree(kvObject KVObject) error {
defer ds.Unlock() defer ds.Unlock()
} }
// cleaup the cache first // cleanup the cache first
if ds.cache != nil { if ds.cache != nil {
// If persistent store is skipped, sequencing needs to // If persistent store is skipped, sequencing needs to
// happen in cache. // happen in cache.

View file

@ -8,8 +8,8 @@ import (
) )
var ( var (
// ErrNotImplmented exported // ErrNotImplemented exported
ErrNotImplmented = errors.New("Functionality not implemented") ErrNotImplemented = errors.New("Functionality not implemented")
) )
// MockData exported // MockData exported
@ -65,7 +65,7 @@ func (s *MockStore) Exists(key string) (bool, error) {
// List gets a range of values at "directory" // List gets a range of values at "directory"
func (s *MockStore) List(prefix string) ([]*store.KVPair, error) { func (s *MockStore) List(prefix string) ([]*store.KVPair, error) {
return nil, ErrNotImplmented return nil, ErrNotImplemented
} }
// DeleteTree deletes a range of values at "directory" // DeleteTree deletes a range of values at "directory"
@ -76,17 +76,17 @@ func (s *MockStore) DeleteTree(prefix string) error {
// Watch a single key for modifications // Watch a single key for modifications
func (s *MockStore) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) { func (s *MockStore) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
return nil, ErrNotImplmented return nil, ErrNotImplemented
} }
// WatchTree triggers a watch on a range of values at "directory" // WatchTree triggers a watch on a range of values at "directory"
func (s *MockStore) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) { func (s *MockStore) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
return nil, ErrNotImplmented return nil, ErrNotImplemented
} }
// NewLock exposed // NewLock exposed
func (s *MockStore) NewLock(key string, options *store.LockOptions) (store.Locker, error) { func (s *MockStore) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
return nil, ErrNotImplmented return nil, ErrNotImplemented
} }
// AtomicPut put a value at "key" if the key has not been // AtomicPut put a value at "key" if the key has not been

View file

@ -9,7 +9,7 @@ import (
"sync/atomic" "sync/atomic"
stackdump "github.com/docker/docker/pkg/signal" stackdump "github.com/docker/docker/pkg/signal"
"github.com/docker/libnetwork/common" "github.com/docker/libnetwork/internal/caller"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -127,7 +127,7 @@ func notImplemented(ctx interface{}, w http.ResponseWriter, r *http.Request) {
rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path)) rsp := WrongCommand("not implemented", fmt.Sprintf("URL path: %s no method implemented check /help\n", r.URL.Path))
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("command not implemented done") log.Info("command not implemented done")
HTTPReply(w, rsp, json) HTTPReply(w, rsp, json)
@ -138,7 +138,7 @@ func help(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := ParseHTTPFormOptions(r) _, json := ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("help done") log.Info("help done")
n, ok := ctx.(*Server) n, ok := ctx.(*Server)
@ -156,7 +156,7 @@ func ready(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := ParseHTTPFormOptions(r) _, json := ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("ready done") log.Info("ready done")
HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), json) HTTPReply(w, CommandSucceed(&StringCmd{Info: "OK"}), json)
} }
@ -166,7 +166,7 @@ func stackTrace(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := ParseHTTPFormOptions(r) _, json := ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("stack trace") log.Info("stack trace")
path, err := stackdump.DumpStacks("/tmp/") path, err := stackdump.DumpStacks("/tmp/")

View file

@ -75,10 +75,10 @@ type Driver interface {
// DecodeTableEntry passes the driver a key, value pair from table it registered // DecodeTableEntry passes the driver a key, value pair from table it registered
// with libnetwork. Driver should return {object ID, map[string]string} tuple. // with libnetwork. Driver should return {object ID, map[string]string} tuple.
// If DecodeTableEntry is called for a table associated with NetworkObject or // If DecodeTableEntry is called for a table associated with NetworkObject or
// EndpointObject the return object ID should be the network id or endppoint id // EndpointObject the return object ID should be the network id or endpoint id
// associated with that entry. map should have information about the object that // associated with that entry. map should have information about the object that
// can be presented to the user. // can be presented to the user.
// For exampe: overlay driver returns the VTEP IP of the host that has the endpoint // For example: overlay driver returns the VTEP IP of the host that has the endpoint
// which is shown in 'network inspect --verbose' // which is shown in 'network inspect --verbose'
DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string)
@ -97,7 +97,7 @@ type NetworkInfo interface {
TableEventRegister(tableName string, objType ObjectType) error TableEventRegister(tableName string, objType ObjectType) error
} }
// InterfaceInfo provides a go interface for drivers to retrive // InterfaceInfo provides a go interface for drivers to retrieve
// network information to interface resources. // network information to interface resources.
type InterfaceInfo interface { type InterfaceInfo interface {
// SetMacAddress allows the driver to set the mac address to the endpoint interface // SetMacAddress allows the driver to set the mac address to the endpoint interface

View file

@ -104,7 +104,7 @@ type containerConfiguration struct {
ChildEndpoints []string ChildEndpoints []string
} }
// cnnectivityConfiguration represents the user specified configuration regarding the external connectivity // connectivityConfiguration represents the user specified configuration regarding the external connectivity
type connectivityConfiguration struct { type connectivityConfiguration struct {
PortBindings []types.PortBinding PortBindings []types.PortBinding
ExposedPorts []types.TransportPort ExposedPorts []types.TransportPort

View file

@ -84,7 +84,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
} }
v4gw, _, err := net.ParseCIDR(s.GwIP) v4gw, _, err := net.ParseCIDR(s.GwIP)
if err != nil { if err != nil {
return fmt.Errorf("gatway %s is not a valid ipv4 address: %v", s.GwIP, err) return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err)
} }
err = jinfo.SetGateway(v4gw) err = jinfo.SetGateway(v4gw)
if err != nil { if err != nil {
@ -101,7 +101,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
} }
v6gw, _, err := net.ParseCIDR(s.GwIP) v6gw, _, err := net.ParseCIDR(s.GwIP)
if err != nil { if err != nil {
return fmt.Errorf("gatway %s is not a valid ipv6 address: %v", s.GwIP, err) return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err)
} }
err = jinfo.SetGatewayIPv6(v6gw) err = jinfo.SetGatewayIPv6(v6gw)
if err != nil { if err != nil {

View file

@ -68,7 +68,7 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
err = d.storeUpdate(config) err = d.storeUpdate(config)
if err != nil { if err != nil {
d.deleteNetwork(config.ID) d.deleteNetwork(config.ID)
logrus.Debugf("encoutered an error rolling back a network create for %s : %v", config.ID, err) logrus.Debugf("encountered an error rolling back a network create for %s : %v", config.ID, err)
return err return err
} }
@ -92,7 +92,7 @@ func (d *driver) createNetwork(config *configuration) error {
return err return err
} }
config.CreatedSlaveLink = true config.CreatedSlaveLink = true
// notify the user in logs they have limited comunicatins // notify the user in logs they have limited communications
if config.Parent == getDummyName(stringid.TruncateID(config.ID)) { if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s", logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
config.Parent) config.Parent)

View file

@ -30,7 +30,7 @@ func createIPVlan(containerIfName, parent, ipvlanMode string) (string, error) {
// Get the link for the master index (Example: the docker host eth iface) // Get the link for the master index (Example: the docker host eth iface)
parentLink, err := ns.NlHandle().LinkByName(parent) parentLink, err := ns.NlHandle().LinkByName(parent)
if err != nil { if err != nil {
return "", fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", ipvlanType, parent, err) return "", fmt.Errorf("error occurred looking up the %s parent iface %s error: %s", ipvlanType, parent, err)
} }
// Create an ipvlan link // Create an ipvlan link
ipvlan := &netlink.IPVlan{ ipvlan := &netlink.IPVlan{
@ -169,7 +169,7 @@ func createDummyLink(dummyName, truncNetID string) error {
} }
parentDummyLink, err := ns.NlHandle().LinkByName(dummyName) parentDummyLink, err := ns.NlHandle().LinkByName(dummyName)
if err != nil { if err != nil {
return fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", ipvlanType, dummyName, err) return fmt.Errorf("error occurred looking up the %s parent iface %s error: %s", ipvlanType, dummyName, err)
} }
// bring the new netlink iface up // bring the new netlink iface up
if err := ns.NlHandle().LinkSetUp(parentDummyLink); err != nil { if err := ns.NlHandle().LinkSetUp(parentDummyLink); err != nil {

View file

@ -31,7 +31,7 @@ func (d *driver) deleteNetwork(nid string) {
d.Unlock() d.Unlock()
} }
// getNetworks Safely returns a slice of existng networks // getNetworks Safely returns a slice of existing networks
func (d *driver) getNetworks() []*network { func (d *driver) getNetworks() []*network {
d.Lock() d.Lock()
defer d.Unlock() defer d.Unlock()

View file

@ -46,7 +46,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
} }
v4gw, _, err := net.ParseCIDR(s.GwIP) v4gw, _, err := net.ParseCIDR(s.GwIP)
if err != nil { if err != nil {
return fmt.Errorf("gatway %s is not a valid ipv4 address: %v", s.GwIP, err) return fmt.Errorf("gateway %s is not a valid ipv4 address: %v", s.GwIP, err)
} }
err = jinfo.SetGateway(v4gw) err = jinfo.SetGateway(v4gw)
if err != nil { if err != nil {
@ -63,7 +63,7 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
} }
v6gw, _, err := net.ParseCIDR(s.GwIP) v6gw, _, err := net.ParseCIDR(s.GwIP)
if err != nil { if err != nil {
return fmt.Errorf("gatway %s is not a valid ipv6 address: %v", s.GwIP, err) return fmt.Errorf("gateway %s is not a valid ipv6 address: %v", s.GwIP, err)
} }
err = jinfo.SetGatewayIPv6(v6gw) err = jinfo.SetGatewayIPv6(v6gw)
if err != nil { if err != nil {

View file

@ -72,7 +72,7 @@ func (d *driver) CreateNetwork(nid string, option map[string]interface{}, nInfo
err = d.storeUpdate(config) err = d.storeUpdate(config)
if err != nil { if err != nil {
d.deleteNetwork(config.ID) d.deleteNetwork(config.ID)
logrus.Debugf("encoutered an error rolling back a network create for %s : %v", config.ID, err) logrus.Debugf("encountered an error rolling back a network create for %s : %v", config.ID, err)
return err return err
} }
@ -96,7 +96,7 @@ func (d *driver) createNetwork(config *configuration) error {
return err return err
} }
config.CreatedSlaveLink = true config.CreatedSlaveLink = true
// notify the user in logs they have limited comunicatins // notify the user in logs they have limited communications
if config.Parent == getDummyName(stringid.TruncateID(config.ID)) { if config.Parent == getDummyName(stringid.TruncateID(config.ID)) {
logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s", logrus.Debugf("Empty -o parent= and --internal flags limit communications to other containers inside of network: %s",
config.Parent) config.Parent)

View file

@ -30,7 +30,7 @@ func createMacVlan(containerIfName, parent, macvlanMode string) (string, error)
// Get the link for the master index (Example: the docker host eth iface) // Get the link for the master index (Example: the docker host eth iface)
parentLink, err := ns.NlHandle().LinkByName(parent) parentLink, err := ns.NlHandle().LinkByName(parent)
if err != nil { if err != nil {
return "", fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", macvlanType, parent, err) return "", fmt.Errorf("error occurred looking up the %s parent iface %s error: %s", macvlanType, parent, err)
} }
// Create a macvlan link // Create a macvlan link
macvlan := &netlink.Macvlan{ macvlan := &netlink.Macvlan{
@ -173,7 +173,7 @@ func createDummyLink(dummyName, truncNetID string) error {
} }
parentDummyLink, err := ns.NlHandle().LinkByName(dummyName) parentDummyLink, err := ns.NlHandle().LinkByName(dummyName)
if err != nil { if err != nil {
return fmt.Errorf("error occoured looking up the %s parent iface %s error: %s", macvlanType, dummyName, err) return fmt.Errorf("error occurred looking up the %s parent iface %s error: %s", macvlanType, dummyName, err)
} }
// bring the new netlink iface up // bring the new netlink iface up
if err := ns.NlHandle().LinkSetUp(parentDummyLink); err != nil { if err := ns.NlHandle().LinkSetUp(parentDummyLink); err != nil {

View file

@ -601,7 +601,7 @@ func (n *network) maxMTU() int {
mtu -= vxlanEncap mtu -= vxlanEncap
if n.secure { if n.secure {
// In case of encryption account for the // In case of encryption account for the
// esp packet espansion and padding // esp packet expansion and padding
mtu -= pktExpansion mtu -= pktExpansion
mtu -= (mtu % 4) mtu -= (mtu % 4)
} }

View file

@ -47,18 +47,10 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err) return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
} }
if err := n.joinSandbox(false); err != nil { if err := n.joinSandbox(s, false, true); err != nil {
return fmt.Errorf("network sandbox join failed: %v", err) return fmt.Errorf("network sandbox join failed: %v", err)
} }
if err := n.joinSubnetSandbox(s, false); 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() sbox := n.sandbox()
overlayIfName, containerIfName, err := createVethPair() overlayIfName, containerIfName, err := createVethPair()

View file

@ -39,7 +39,7 @@ var (
type networkTable map[string]*network type networkTable map[string]*network
type subnet struct { type subnet struct {
once *sync.Once sboxInit bool
vxlanName string vxlanName string
brName string brName string
vni uint32 vni uint32
@ -63,7 +63,7 @@ type network struct {
endpoints endpointTable endpoints endpointTable
driver *driver driver *driver
joinCnt int joinCnt int
once *sync.Once sboxInit bool
initEpoch int initEpoch int
initErr error initErr error
subnets []*subnet subnets []*subnet
@ -150,7 +150,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
id: id, id: id,
driver: d, driver: d,
endpoints: endpointTable{}, endpoints: endpointTable{},
once: &sync.Once{},
subnets: []*subnet{}, subnets: []*subnet{},
} }
@ -193,7 +192,6 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
s := &subnet{ s := &subnet{
subnetIP: ipd.Pool, subnetIP: ipd.Pool,
gwIP: ipd.Gateway, gwIP: ipd.Gateway,
once: &sync.Once{},
} }
if len(vnis) != 0 { if len(vnis) != 0 {
@ -277,7 +275,7 @@ func (d *driver) DeleteNetwork(nid string) error {
logrus.Warnf("Failed to delete overlay endpoint %.7s from local store: %v", ep.id, err) logrus.Warnf("Failed to delete overlay endpoint %.7s from local store: %v", ep.id, err)
} }
} }
// flush the peerDB entries
doPeerFlush = true doPeerFlush = true
delete(d.networks, nid) delete(d.networks, nid)
@ -304,29 +302,54 @@ func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
return nil return nil
} }
func (n *network) incEndpointCount() { func (n *network) joinSandbox(s *subnet, restore bool, incJoinCount bool) error {
n.Lock()
defer n.Unlock()
n.joinCnt++
}
func (n *network) joinSandbox(restore bool) error {
// If there is a race between two go routines here only one will win // If there is a race between two go routines here only one will win
// the other will wait. // the other will wait.
n.once.Do(func() { networkOnce.Do(networkOnceInit)
// save the error status of initSandbox in n.initErr so that
// all the racing go routines are able to know the status. n.Lock()
// If non-restore initialization occurred and was successful then
// tell the peerDB to initialize the sandbox with all the peers
// previously received from networkdb. But only do this after
// unlocking the network. Otherwise we could deadlock with
// on the peerDB channel while peerDB is waiting for the network lock.
var doInitPeerDB bool
defer func() {
n.Unlock()
if doInitPeerDB {
n.driver.initSandboxPeerDB(n.id)
}
}()
if !n.sboxInit {
n.initErr = n.initSandbox(restore) n.initErr = n.initSandbox(restore)
}) doInitPeerDB = n.initErr == nil && !restore
// If there was an error, we cannot recover it
n.sboxInit = true
}
return n.initErr if n.initErr != nil {
} return fmt.Errorf("network sandbox join failed: %v", n.initErr)
}
func (n *network) joinSubnetSandbox(s *subnet, restore bool) error { subnetErr := s.initErr
s.once.Do(func() { if !s.sboxInit {
s.initErr = n.initSubnetSandbox(s, restore) subnetErr = n.initSubnetSandbox(s, restore)
}) // We can recover from these errors, but not on restore
return s.initErr if restore || subnetErr == nil {
s.initErr = subnetErr
s.sboxInit = true
}
}
if subnetErr != nil {
return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), subnetErr)
}
if incJoinCount {
n.joinCnt++
}
return nil
} }
func (n *network) leaveSandbox() { func (n *network) leaveSandbox() {
@ -337,15 +360,14 @@ func (n *network) leaveSandbox() {
return return
} }
// We are about to destroy sandbox since the container is leaving the network
// 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.destroySandbox() n.destroySandbox()
n.sboxInit = false
n.initErr = nil
for _, s := range n.subnets {
s.sboxInit = false
s.initErr = nil
}
} }
// to be called while holding network lock // to be called while holding network lock
@ -478,7 +500,7 @@ func (n *network) generateVxlanName(s *subnet) string {
id = n.id[:5] id = n.id[:5]
} }
return "vx-" + fmt.Sprintf("%06x", n.vxlanID(s)) + "-" + id return fmt.Sprintf("vx-%06x-%v", s.vni, id)
} }
func (n *network) generateBridgeName(s *subnet) string { func (n *network) generateBridgeName(s *subnet) string {
@ -491,7 +513,7 @@ func (n *network) generateBridgeName(s *subnet) string {
} }
func (n *network) getBridgeNamePrefix(s *subnet) string { func (n *network) getBridgeNamePrefix(s *subnet) string {
return "ov-" + fmt.Sprintf("%06x", n.vxlanID(s)) return fmt.Sprintf("ov-%06x", s.vni)
} }
func checkOverlap(nw *net.IPNet) error { func checkOverlap(nw *net.IPNet) error {
@ -513,7 +535,7 @@ func checkOverlap(nw *net.IPNet) error {
} }
func (n *network) restoreSubnetSandbox(s *subnet, brName, vxlanName string) error { func (n *network) restoreSubnetSandbox(s *subnet, brName, vxlanName string) error {
sbox := n.sandbox() sbox := n.sbox
// restore overlay osl sandbox // restore overlay osl sandbox
Ifaces := make(map[string][]osl.IfaceOption) Ifaces := make(map[string][]osl.IfaceOption)
@ -542,7 +564,7 @@ func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error
deleteInterfaceBySubnet(n.getBridgeNamePrefix(s), s) deleteInterfaceBySubnet(n.getBridgeNamePrefix(s), s)
} }
// Try to delete the vxlan interface by vni if already present // Try to delete the vxlan interface by vni if already present
deleteVxlanByVNI("", n.vxlanID(s)) deleteVxlanByVNI("", s.vni)
if err := checkOverlap(s.subnetIP); err != nil { if err := checkOverlap(s.subnetIP); err != nil {
return err return err
@ -556,24 +578,24 @@ func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error
// it must a stale namespace from previous // it must a stale namespace from previous
// life. Destroy it completely and reclaim resourced. // life. Destroy it completely and reclaim resourced.
networkMu.Lock() networkMu.Lock()
path, ok := vniTbl[n.vxlanID(s)] path, ok := vniTbl[s.vni]
networkMu.Unlock() networkMu.Unlock()
if ok { if ok {
deleteVxlanByVNI(path, n.vxlanID(s)) deleteVxlanByVNI(path, s.vni)
if err := syscall.Unmount(path, syscall.MNT_FORCE); err != nil { if err := syscall.Unmount(path, syscall.MNT_FORCE); err != nil {
logrus.Errorf("unmount of %s failed: %v", path, err) logrus.Errorf("unmount of %s failed: %v", path, err)
} }
os.Remove(path) os.Remove(path)
networkMu.Lock() networkMu.Lock()
delete(vniTbl, n.vxlanID(s)) delete(vniTbl, s.vni)
networkMu.Unlock() networkMu.Unlock()
} }
} }
// create a bridge and vxlan device for this subnet and move it to the sandbox // create a bridge and vxlan device for this subnet and move it to the sandbox
sbox := n.sandbox() sbox := n.sbox
if err := sbox.AddInterface(brName, "br", if err := sbox.AddInterface(brName, "br",
sbox.InterfaceOptions().Address(s.gwIP), sbox.InterfaceOptions().Address(s.gwIP),
@ -581,13 +603,30 @@ func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error
return fmt.Errorf("bridge creation in sandbox failed for subnet %q: %v", s.subnetIP.String(), err) return fmt.Errorf("bridge creation in sandbox failed for subnet %q: %v", s.subnetIP.String(), err)
} }
err := createVxlan(vxlanName, n.vxlanID(s), n.maxMTU()) err := createVxlan(vxlanName, s.vni, n.maxMTU())
if err != nil { if err != nil {
return err return err
} }
if err := sbox.AddInterface(vxlanName, "vxlan", if err := sbox.AddInterface(vxlanName, "vxlan",
sbox.InterfaceOptions().Master(brName)); err != nil { sbox.InterfaceOptions().Master(brName)); err != nil {
// If adding vxlan device to the overlay namespace fails, remove the bridge interface we
// already added to the namespace. This allows the caller to try the setup again.
for _, iface := range sbox.Info().Interfaces() {
if iface.SrcName() == brName {
if ierr := iface.Remove(); ierr != nil {
logrus.Errorf("removing bridge failed from ov ns %v failed, %v", n.sbox.Key(), ierr)
}
}
}
// Also, delete the vxlan interface. Since a global vni id is associated
// with the vxlan interface, an orphaned vxlan interface will result in
// failure of vxlan device creation if the vni is assigned to some other
// network.
if deleteErr := deleteInterface(vxlanName); deleteErr != nil {
logrus.Warnf("could not delete vxlan interface, %s, error %v, after config error, %v", vxlanName, deleteErr, err)
}
return fmt.Errorf("vxlan interface creation failed for subnet %q: %v", s.subnetIP.String(), err) return fmt.Errorf("vxlan interface creation failed for subnet %q: %v", s.subnetIP.String(), err)
} }
@ -619,6 +658,7 @@ func (n *network) setupSubnetSandbox(s *subnet, brName, vxlanName string) error
return nil return nil
} }
// Must be called with the network lock
func (n *network) initSubnetSandbox(s *subnet, restore bool) error { func (n *network) initSubnetSandbox(s *subnet, restore bool) error {
brName := n.generateBridgeName(s) brName := n.generateBridgeName(s)
vxlanName := n.generateVxlanName(s) vxlanName := n.generateVxlanName(s)
@ -633,10 +673,8 @@ func (n *network) initSubnetSandbox(s *subnet, restore bool) error {
} }
} }
n.Lock()
s.vxlanName = vxlanName s.vxlanName = vxlanName
s.brName = brName s.brName = brName
n.Unlock()
return nil return nil
} }
@ -677,11 +715,7 @@ func (n *network) cleanupStaleSandboxes() {
} }
func (n *network) initSandbox(restore bool) error { func (n *network) initSandbox(restore bool) error {
n.Lock()
n.initEpoch++ n.initEpoch++
n.Unlock()
networkOnce.Do(networkOnceInit)
if !restore { if !restore {
if hostMode { if hostMode {
@ -711,12 +745,7 @@ func (n *network) initSandbox(restore bool) error {
} }
// this is needed to let the peerAdd configure the sandbox // this is needed to let the peerAdd configure the sandbox
n.setSandbox(sbox) n.sbox = sbox
if !restore {
// Initialize the sandbox with all the peers previously received from networkdb
n.driver.initSandboxPeerDB(n.id)
}
// If we are in swarm mode, we don't need anymore the watchMiss routine. // If we are in swarm mode, we don't need anymore the watchMiss routine.
// This will save 1 thread and 1 netlink socket per network // This will save 1 thread and 1 netlink socket per network
@ -734,7 +763,7 @@ func (n *network) initSandbox(restore bool) error {
tv := syscall.NsecToTimeval(soTimeout.Nanoseconds()) tv := syscall.NsecToTimeval(soTimeout.Nanoseconds())
err = nlSock.SetReceiveTimeout(&tv) err = nlSock.SetReceiveTimeout(&tv)
}) })
n.setNetlinkSocket(nlSock) n.nlSocket = nlSock
if err == nil { if err == nil {
go n.watchMiss(nlSock, key) go n.watchMiss(nlSock, key)
@ -836,7 +865,6 @@ func (d *driver) restoreNetworkFromStore(nid string) *network {
if n != nil { if n != nil {
n.driver = d n.driver = d
n.endpoints = endpointTable{} n.endpoints = endpointTable{}
n.once = &sync.Once{}
d.networks[nid] = n d.networks[nid] = n
} }
return n return n
@ -844,11 +872,11 @@ func (d *driver) restoreNetworkFromStore(nid string) *network {
func (d *driver) network(nid string) *network { func (d *driver) network(nid string) *network {
d.Lock() d.Lock()
defer d.Unlock()
n, ok := d.networks[nid] n, ok := d.networks[nid]
if !ok { if !ok {
n = d.restoreNetworkFromStore(nid) n = d.restoreNetworkFromStore(nid)
} }
d.Unlock()
return n return n
} }
@ -869,26 +897,12 @@ func (d *driver) getNetworkFromStore(nid string) *network {
func (n *network) sandbox() osl.Sandbox { func (n *network) sandbox() osl.Sandbox {
n.Lock() n.Lock()
defer n.Unlock() defer n.Unlock()
return n.sbox return n.sbox
} }
func (n *network) setSandbox(sbox osl.Sandbox) {
n.Lock()
n.sbox = sbox
n.Unlock()
}
func (n *network) setNetlinkSocket(nlSk *nl.NetlinkSocket) {
n.Lock()
n.nlSocket = nlSk
n.Unlock()
}
func (n *network) vxlanID(s *subnet) uint32 { func (n *network) vxlanID(s *subnet) uint32 {
n.Lock() n.Lock()
defer n.Unlock() defer n.Unlock()
return s.vni return s.vni
} }
@ -997,7 +1011,6 @@ func (n *network) SetValue(value []byte) error {
subnetIP: subnetIP, subnetIP: subnetIP,
gwIP: gwIP, gwIP: gwIP,
vni: vni, vni: vni,
once: &sync.Once{},
} }
n.subnets = append(n.subnets, s) n.subnets = append(n.subnets, s)
} else { } else {
@ -1023,7 +1036,10 @@ func (n *network) writeToStore() error {
} }
func (n *network) releaseVxlanID() ([]uint32, error) { func (n *network) releaseVxlanID() ([]uint32, error) {
if len(n.subnets) == 0 { n.Lock()
nSubnets := len(n.subnets)
n.Unlock()
if nSubnets == 0 {
return nil, nil return nil, nil
} }
@ -1039,14 +1055,17 @@ func (n *network) releaseVxlanID() ([]uint32, error) {
} }
} }
var vnis []uint32 var vnis []uint32
n.Lock()
for _, s := range n.subnets { for _, s := range n.subnets {
if n.driver.vxlanIdm != nil { if n.driver.vxlanIdm != nil {
vni := n.vxlanID(s) vnis = append(vnis, s.vni)
vnis = append(vnis, vni)
n.driver.vxlanIdm.Release(uint64(vni))
} }
s.vni = 0
}
n.Unlock()
n.setVxlanID(s, 0) for _, vni := range vnis {
n.driver.vxlanIdm.Release(uint64(vni))
} }
return vnis, nil return vnis, nil
@ -1054,7 +1073,7 @@ func (n *network) releaseVxlanID() ([]uint32, error) {
func (n *network) obtainVxlanID(s *subnet) error { func (n *network) obtainVxlanID(s *subnet) error {
//return if the subnet already has a vxlan id assigned //return if the subnet already has a vxlan id assigned
if s.vni != 0 { if n.vxlanID(s) != 0 {
return nil return nil
} }
@ -1067,7 +1086,7 @@ func (n *network) obtainVxlanID(s *subnet) error {
return fmt.Errorf("getting network %q from datastore failed %v", n.id, err) return fmt.Errorf("getting network %q from datastore failed %v", n.id, err)
} }
if s.vni == 0 { if n.vxlanID(s) == 0 {
vxlanID, err := n.driver.vxlanIdm.GetID(true) vxlanID, err := n.driver.vxlanIdm.GetID(true)
if err != nil { if err != nil {
return fmt.Errorf("failed to allocate vxlan id: %v", err) return fmt.Errorf("failed to allocate vxlan id: %v", err)

View file

@ -105,17 +105,6 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
logrus.Warnf("Failure during overlay endpoints restore: %v", err) logrus.Warnf("Failure during overlay endpoints restore: %v", err)
} }
// If an error happened when the network join the sandbox during the endpoints restore
// we should reset it now along with the once variable, so that subsequent endpoint joins
// outside of the restore path can potentially fix the network join and succeed.
for nid, n := range d.networks {
if n.initErr != nil {
logrus.Infof("resetting init error and once variable for network %s after unsuccessful endpoint restore: %v", nid, n.initErr)
n.initErr = nil
n.once = &sync.Once{}
}
}
return dc.RegisterDriver(networkType, d, c) return dc.RegisterDriver(networkType, d, c)
} }
@ -151,14 +140,10 @@ func (d *driver) restoreEndpoints() error {
return fmt.Errorf("could not find subnet for endpoint %s", ep.id) return fmt.Errorf("could not find subnet for endpoint %s", ep.id)
} }
if err := n.joinSandbox(true); err != nil { if err := n.joinSandbox(s, true, true); err != nil {
return fmt.Errorf("restore network sandbox failed: %v", err) return fmt.Errorf("restore network sandbox failed: %v", err)
} }
if err := n.joinSubnetSandbox(s, true); err != nil {
return fmt.Errorf("restore subnet sandbox failed for %q: %v", s.subnetIP.String(), err)
}
Ifaces := make(map[string][]osl.IfaceOption) Ifaces := make(map[string][]osl.IfaceOption)
vethIfaceOption := make([]osl.IfaceOption, 1) vethIfaceOption := make([]osl.IfaceOption, 1)
vethIfaceOption = append(vethIfaceOption, n.sbox.InterfaceOptions().Master(s.brName)) vethIfaceOption = append(vethIfaceOption, n.sbox.InterfaceOptions().Master(s.brName))
@ -166,10 +151,10 @@ func (d *driver) restoreEndpoints() error {
err := n.sbox.Restore(Ifaces, nil, nil, nil) err := n.sbox.Restore(Ifaces, nil, nil, nil)
if err != nil { if err != nil {
n.leaveSandbox()
return fmt.Errorf("failed to restore overlay sandbox: %v", err) return fmt.Errorf("failed to restore overlay sandbox: %v", err)
} }
n.incEndpointCount()
d.peerAdd(ep.nid, ep.id, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), false, false, true) d.peerAdd(ep.nid, ep.id, ep.addr.IP, ep.addr.Mask, ep.mac, net.ParseIP(d.advertiseAddress), false, false, true)
} }
return nil return nil

View file

@ -7,7 +7,8 @@ import (
"sync" "sync"
"syscall" "syscall"
"github.com/docker/libnetwork/common" "github.com/docker/libnetwork/internal/caller"
"github.com/docker/libnetwork/internal/setmatrix"
"github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/osl"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -59,7 +60,7 @@ func (p *peerEntryDB) UnMarshalDB() peerEntry {
type peerMap struct { type peerMap struct {
// set of peerEntry, note they have to be objects and not pointers to maintain the proper equality checks // set of peerEntry, note they have to be objects and not pointers to maintain the proper equality checks
mp common.SetMatrix mp setmatrix.SetMatrix
sync.Mutex sync.Mutex
} }
@ -170,7 +171,7 @@ func (d *driver) peerDbAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask
pMap, ok := d.peerDb.mp[nid] pMap, ok := d.peerDb.mp[nid]
if !ok { if !ok {
d.peerDb.mp[nid] = &peerMap{ d.peerDb.mp[nid] = &peerMap{
mp: common.NewSetMatrix(), mp: setmatrix.NewSetMatrix(),
} }
pMap = d.peerDb.mp[nid] pMap = d.peerDb.mp[nid]
@ -297,7 +298,7 @@ func (d *driver) peerOpRoutine(ctx context.Context, ch chan *peerOperation) {
} }
func (d *driver) peerInit(nid string) { func (d *driver) peerInit(nid string) {
callerName := common.CallerName(1) callerName := caller.Name(1)
d.peerOpCh <- &peerOperation{ d.peerOpCh <- &peerOperation{
opType: peerOperationINIT, opType: peerOperationINIT,
networkID: nid, networkID: nid,
@ -331,7 +332,7 @@ func (d *driver) peerAdd(nid, eid string, peerIP net.IP, peerIPMask net.IPMask,
l2Miss: l2Miss, l2Miss: l2Miss,
l3Miss: l3Miss, l3Miss: l3Miss,
localPeer: localPeer, localPeer: localPeer,
callerName: common.CallerName(1), callerName: caller.Name(1),
} }
} }
@ -384,7 +385,7 @@ func (d *driver) peerAddOp(nid, eid string, peerIP net.IP, peerIPMask net.IPMask
return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err) return fmt.Errorf("couldn't get vxlan id for %q: %v", s.subnetIP.String(), err)
} }
if err := n.joinSubnetSandbox(s, false); err != nil { if err := n.joinSandbox(s, false, false); err != nil {
return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err) return fmt.Errorf("subnet sandbox join failed for %q: %v", s.subnetIP.String(), err)
} }
@ -422,7 +423,7 @@ func (d *driver) peerDelete(nid, eid string, peerIP net.IP, peerIPMask net.IPMas
peerIPMask: peerIPMask, peerIPMask: peerIPMask,
peerMac: peerMac, peerMac: peerMac,
vtepIP: vtep, vtepIP: vtep,
callerName: common.CallerName(1), callerName: caller.Name(1),
localPeer: localPeer, localPeer: localPeer,
} }
} }
@ -491,7 +492,7 @@ func (d *driver) peerFlush(nid string) {
d.peerOpCh <- &peerOperation{ d.peerOpCh <- &peerOperation{
opType: peerOperationFLUSH, opType: peerOperationFLUSH,
networkID: nid, networkID: nid,
callerName: common.CallerName(1), callerName: caller.Name(1),
} }
} }

View file

@ -150,7 +150,7 @@ type JoinRequest struct {
Options map[string]interface{} Options map[string]interface{}
} }
// InterfaceName is the struct represetation of a pair of devices with source // InterfaceName is the struct representation of a pair of devices with source
// and destination, for the purposes of putting an endpoint into a container. // and destination, for the purposes of putting an endpoint into a container.
type InterfaceName struct { type InterfaceName struct {
SrcName string SrcName string

View file

@ -54,7 +54,7 @@ type IPAMNotifyFunc func(name string, driver ipamapi.Ipam, cap *ipamapi.Capabili
// DriverNotifyFunc defines the notify function signature when a new network driver gets registered. // DriverNotifyFunc defines the notify function signature when a new network driver gets registered.
type DriverNotifyFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) error type DriverNotifyFunc func(name string, driver driverapi.Driver, capability driverapi.Capability) error
// New retruns a new driver registry handle. // New returns a new driver registry handle.
func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc, pg plugingetter.PluginGetter) (*DrvRegistry, error) { func New(lDs, gDs interface{}, dfn DriverNotifyFunc, ifn IPAMNotifyFunc, pg plugingetter.PluginGetter) (*DrvRegistry, error) {
r := &DrvRegistry{ r := &DrvRegistry{
drivers: make(driverTable), drivers: make(driverTable),

View file

@ -1,4 +1,4 @@
package common package caller
import ( import (
"runtime" "runtime"
@ -11,7 +11,7 @@ func callerInfo(i int) string {
if ok { if ok {
f := runtime.FuncForPC(ptr) f := runtime.FuncForPC(ptr)
if f != nil { if f != nil {
// f.Name() is like: github.com/docker/libnetwork/common.MethodName // f.Name() is like: github.com/docker/libnetwork/caller.MethodName
tmp := strings.Split(f.Name(), ".") tmp := strings.Split(f.Name(), ".")
if len(tmp) > 0 { if len(tmp) > 0 {
fName = tmp[len(tmp)-1] fName = tmp[len(tmp)-1]
@ -22,8 +22,8 @@ func callerInfo(i int) string {
return fName return fName
} }
// CallerName returns the name of the function at the specified level // Name returns the name of the function at the specified level
// level == 0 means current method name // level == 0 means current method name
func CallerName(level int) string { func Name(level int) string {
return callerInfo(2 + level) return callerInfo(2 + level)
} }

View file

@ -1,4 +1,4 @@
package common package setmatrix
import ( import (
"sync" "sync"

View file

@ -66,7 +66,7 @@ func newConnection() (*Conn, error) {
return c, nil return c, nil
} }
// Innitialize D-Bus connection. // Initialize D-Bus connection.
func (c *Conn) initConnection() error { func (c *Conn) initConnection() error {
var err error var err error

View file

@ -477,7 +477,7 @@ func raw(args ...string) ([]byte, error) {
return filterOutput(startTime, output, args...), err return filterOutput(startTime, output, args...), err
} }
// RawCombinedOutput inernally calls the Raw function and returns a non nil // RawCombinedOutput internally calls the Raw function and returns a non nil
// error if Raw returned a non nil error or a non empty output // error if Raw returned a non nil error or a non empty output
func RawCombinedOutput(args ...string) error { func RawCombinedOutput(args ...string) error {
if output, err := Raw(args...); err != nil || len(output) != 0 { if output, err := Raw(args...); err != nil || len(output) != 0 {

View file

@ -100,7 +100,7 @@ func fillService(s *Service) nl.NetlinkRequestData {
return cmdAttr return cmdAttr
} }
func fillDestinaton(d *Destination) nl.NetlinkRequestData { func fillDestination(d *Destination) nl.NetlinkRequestData {
cmdAttr := nl.NewRtAttr(ipvsCmdAttrDest, nil) cmdAttr := nl.NewRtAttr(ipvsCmdAttrDest, nil)
nl.NewRtAttrChild(cmdAttr, ipvsDestAttrAddress, rawIPData(d.Address)) nl.NewRtAttrChild(cmdAttr, ipvsDestAttrAddress, rawIPData(d.Address))
@ -134,7 +134,7 @@ func (i *Handle) doCmdwithResponse(s *Service, d *Destination, cmd uint8) ([][]b
} }
} else { } else {
req.AddData(fillDestinaton(d)) req.AddData(fillDestination(d))
} }
res, err := execute(i.sock, req, 0) res, err := execute(i.sock, req, 0)

View file

@ -9,11 +9,11 @@ import (
"time" "time"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/libnetwork/common"
"github.com/docker/libnetwork/config" "github.com/docker/libnetwork/config"
"github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/driverapi" "github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/etchosts"
"github.com/docker/libnetwork/internal/setmatrix"
"github.com/docker/libnetwork/ipamapi" "github.com/docker/libnetwork/ipamapi"
"github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils" "github.com/docker/libnetwork/netutils"
@ -88,7 +88,7 @@ type NetworkInfo interface {
type EndpointWalker func(ep Endpoint) bool type EndpointWalker func(ep Endpoint) bool
// ipInfo is the reverse mapping from IP to service name to serve the PTR query. // ipInfo is the reverse mapping from IP to service name to serve the PTR query.
// extResolver is set if an externl server resolves a service name to this IP. // extResolver is set if an external server resolves a service name to this IP.
// Its an indication to defer PTR queries also to that external server. // Its an indication to defer PTR queries also to that external server.
type ipInfo struct { type ipInfo struct {
name string name string
@ -104,9 +104,9 @@ type svcMapEntry struct {
} }
type svcInfo struct { type svcInfo struct {
svcMap common.SetMatrix svcMap setmatrix.SetMatrix
svcIPv6Map common.SetMatrix svcIPv6Map setmatrix.SetMatrix
ipMap common.SetMatrix ipMap setmatrix.SetMatrix
service map[string][]servicePorts service map[string][]servicePorts
} }
@ -1353,7 +1353,7 @@ func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool
} }
} }
func addIPToName(ipMap common.SetMatrix, name, serviceID string, ip net.IP) { func addIPToName(ipMap setmatrix.SetMatrix, name, serviceID string, ip net.IP) {
reverseIP := netutils.ReverseIP(ip.String()) reverseIP := netutils.ReverseIP(ip.String())
ipMap.Insert(reverseIP, ipInfo{ ipMap.Insert(reverseIP, ipInfo{
name: name, name: name,
@ -1361,7 +1361,7 @@ func addIPToName(ipMap common.SetMatrix, name, serviceID string, ip net.IP) {
}) })
} }
func delIPToName(ipMap common.SetMatrix, name, serviceID string, ip net.IP) { func delIPToName(ipMap setmatrix.SetMatrix, name, serviceID string, ip net.IP) {
reverseIP := netutils.ReverseIP(ip.String()) reverseIP := netutils.ReverseIP(ip.String())
ipMap.Remove(reverseIP, ipInfo{ ipMap.Remove(reverseIP, ipInfo{
name: name, name: name,
@ -1369,14 +1369,14 @@ func delIPToName(ipMap common.SetMatrix, name, serviceID string, ip net.IP) {
}) })
} }
func addNameToIP(svcMap common.SetMatrix, name, serviceID string, epIP net.IP) { func addNameToIP(svcMap setmatrix.SetMatrix, name, serviceID string, epIP net.IP) {
svcMap.Insert(name, svcMapEntry{ svcMap.Insert(name, svcMapEntry{
ip: epIP.String(), ip: epIP.String(),
serviceID: serviceID, serviceID: serviceID,
}) })
} }
func delNameToIP(svcMap common.SetMatrix, name, serviceID string, epIP net.IP) { func delNameToIP(svcMap setmatrix.SetMatrix, name, serviceID string, epIP net.IP) {
svcMap.Remove(name, svcMapEntry{ svcMap.Remove(name, svcMapEntry{
ip: epIP.String(), ip: epIP.String(),
serviceID: serviceID, serviceID: serviceID,
@ -1399,9 +1399,9 @@ func (n *network) addSvcRecords(eID, name, serviceID string, epIP, epIPv6 net.IP
sr, ok := c.svcRecords[n.ID()] sr, ok := c.svcRecords[n.ID()]
if !ok { if !ok {
sr = svcInfo{ sr = svcInfo{
svcMap: common.NewSetMatrix(), svcMap: setmatrix.NewSetMatrix(),
svcIPv6Map: common.NewSetMatrix(), svcIPv6Map: setmatrix.NewSetMatrix(),
ipMap: common.NewSetMatrix(), ipMap: setmatrix.NewSetMatrix(),
} }
c.svcRecords[n.ID()] = sr c.svcRecords[n.ID()] = sr
} }
@ -1654,7 +1654,7 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name()) return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name())
} }
if !d.Pool.Contains(ip) { if !d.Pool.Contains(ip) {
return types.ForbiddenErrorf("auxilairy address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool) return types.ForbiddenErrorf("auxiliary address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool)
} }
// Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool // Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool
if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange { if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange {
@ -2036,7 +2036,7 @@ func (n *network) ResolveService(name string) ([]*net.SRV, []net.IP) {
logrus.Debugf("Service name To resolve: %v", name) logrus.Debugf("Service name To resolve: %v", name)
// There are DNS implementaions that allow SRV queries for names not in // There are DNS implementations that allow SRV queries for names not in
// the format defined by RFC 2782. Hence specific validations checks are // the format defined by RFC 2782. Hence specific validations checks are
// not done // not done
parts := strings.Split(name, ".") parts := strings.Split(name, ".")
@ -2126,7 +2126,7 @@ func (n *network) lbEndpointName() string {
func (n *network) createLoadBalancerSandbox() (retErr error) { func (n *network) createLoadBalancerSandbox() (retErr error) {
sandboxName := n.lbSandboxName() sandboxName := n.lbSandboxName()
// Mark the sandbox to be a load balancer // Mark the sandbox to be a load balancer
sbOptions := []SandboxOption{OptionLoadBalancer()} sbOptions := []SandboxOption{OptionLoadBalancer(n.id)}
if n.ingress { if n.ingress {
sbOptions = append(sbOptions, OptionIngress()) sbOptions = append(sbOptions, OptionIngress())
} }

View file

@ -243,7 +243,7 @@ func (nDB *NetworkDB) clusterLeave() error {
} }
func (nDB *NetworkDB) triggerFunc(stagger time.Duration, C <-chan time.Time, f func()) { func (nDB *NetworkDB) triggerFunc(stagger time.Duration, C <-chan time.Time, f func()) {
// Use a random stagger to avoid syncronizing // Use a random stagger to avoid synchronizing
randStagger := time.Duration(uint64(rnd.Int63()) % uint64(stagger)) randStagger := time.Duration(uint64(rnd.Int63()) % uint64(stagger))
select { select {
case <-time.After(randStagger): case <-time.After(randStagger):

View file

@ -58,7 +58,7 @@ type NetworkDB struct {
// List of all peer nodes which have left // List of all peer nodes which have left
leftNodes map[string]*node leftNodes map[string]*node
// A multi-dimensional map of network/node attachmemts. The // A multi-dimensional map of network/node attachments. The
// first key is a node name and the second key is a network ID // first key is a node name and the second key is a network ID
// for the network that node is participating in. // for the network that node is participating in.
networks map[string]map[string]*network networks map[string]map[string]*network
@ -153,7 +153,7 @@ type network struct {
entriesNumber int entriesNumber int
} }
// Config represents the configuration of the networdb instance and // Config represents the configuration of the networkdb instance and
// can be passed by the caller. // can be passed by the caller.
type Config struct { type Config struct {
// NodeID is the node unique identifier of the node when is part of the cluster // NodeID is the node unique identifier of the node when is part of the cluster

View file

@ -48,7 +48,7 @@ type MessageType int32
const ( const (
MessageTypeInvalid MessageType = 0 MessageTypeInvalid MessageType = 0
// NetworEvent message type is used to communicate network // NetworkEvent message type is used to communicate network
// attachments on the node. // attachments on the node.
MessageTypeNetworkEvent MessageType = 1 MessageTypeNetworkEvent MessageType = 1
// TableEvent message type is used to communicate any table // TableEvent message type is used to communicate any table
@ -66,7 +66,7 @@ const (
// which is a pack of many message of above types, packed into // which is a pack of many message of above types, packed into
// a single compound message. // a single compound message.
MessageTypeCompound MessageType = 5 MessageTypeCompound MessageType = 5
// NodeEvent message type is used to communicare node // NodeEvent message type is used to communicate node
// join/leave events in the cluster // join/leave events in the cluster
MessageTypeNodeEvent MessageType = 6 MessageTypeNodeEvent MessageType = 6
) )

View file

@ -19,7 +19,7 @@ enum MessageType {
INVALID = 0 [(gogoproto.enumvalue_customname) = "MessageTypeInvalid"]; INVALID = 0 [(gogoproto.enumvalue_customname) = "MessageTypeInvalid"];
// NetworEvent message type is used to communicate network // NetworkEvent message type is used to communicate network
// attachments on the node. // attachments on the node.
NETWORK_EVENT = 1 [(gogoproto.enumvalue_customname) = "MessageTypeNetworkEvent"]; NETWORK_EVENT = 1 [(gogoproto.enumvalue_customname) = "MessageTypeNetworkEvent"];
@ -42,7 +42,7 @@ enum MessageType {
// a single compound message. // a single compound message.
COMPOUND = 5 [(gogoproto.enumvalue_customname) = "MessageTypeCompound"]; COMPOUND = 5 [(gogoproto.enumvalue_customname) = "MessageTypeCompound"];
// NodeEvent message type is used to communicare node // NodeEvent message type is used to communicate node
// join/leave events in the cluster // join/leave events in the cluster
NODE_EVENT = 6 [(gogoproto.enumvalue_customname) = "MessageTypeNodeEvent"]; NODE_EVENT = 6 [(gogoproto.enumvalue_customname) = "MessageTypeNodeEvent"];
} }

View file

@ -6,8 +6,8 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/docker/libnetwork/common"
"github.com/docker/libnetwork/diagnostic" "github.com/docker/libnetwork/diagnostic"
"github.com/docker/libnetwork/internal/caller"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -37,7 +37,7 @@ func dbJoin(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("join cluster") log.Info("join cluster")
if len(r.Form["members"]) < 1 { if len(r.Form["members"]) < 1 {
@ -70,7 +70,7 @@ func dbPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("network peers") log.Info("network peers")
if len(r.Form["nid"]) < 1 { if len(r.Form["nid"]) < 1 {
@ -104,7 +104,7 @@ func dbClusterPeers(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("cluster peers") log.Info("cluster peers")
nDB, ok := ctx.(*NetworkDB) nDB, ok := ctx.(*NetworkDB)
@ -127,7 +127,7 @@ func dbCreateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
unsafe, json := diagnostic.ParseHTTPFormOptions(r) unsafe, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("create entry") log.Info("create entry")
if len(r.Form["tname"]) < 1 || if len(r.Form["tname"]) < 1 ||
@ -176,7 +176,7 @@ func dbUpdateEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
unsafe, json := diagnostic.ParseHTTPFormOptions(r) unsafe, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("update entry") log.Info("update entry")
if len(r.Form["tname"]) < 1 || if len(r.Form["tname"]) < 1 ||
@ -224,7 +224,7 @@ func dbDeleteEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("delete entry") log.Info("delete entry")
if len(r.Form["tname"]) < 1 || if len(r.Form["tname"]) < 1 ||
@ -261,7 +261,7 @@ func dbGetEntry(ctx interface{}, w http.ResponseWriter, r *http.Request) {
unsafe, json := diagnostic.ParseHTTPFormOptions(r) unsafe, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("get entry") log.Info("get entry")
if len(r.Form["tname"]) < 1 || if len(r.Form["tname"]) < 1 ||
@ -307,7 +307,7 @@ func dbJoinNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("join network") log.Info("join network")
if len(r.Form["nid"]) < 1 { if len(r.Form["nid"]) < 1 {
@ -339,7 +339,7 @@ func dbLeaveNetwork(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("leave network") log.Info("leave network")
if len(r.Form["nid"]) < 1 { if len(r.Form["nid"]) < 1 {
@ -371,7 +371,7 @@ func dbGetTable(ctx interface{}, w http.ResponseWriter, r *http.Request) {
unsafe, json := diagnostic.ParseHTTPFormOptions(r) unsafe, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("get table") log.Info("get table")
if len(r.Form["tname"]) < 1 || if len(r.Form["tname"]) < 1 ||
@ -419,7 +419,7 @@ func dbNetworkStats(ctx interface{}, w http.ResponseWriter, r *http.Request) {
_, json := diagnostic.ParseHTTPFormOptions(r) _, json := diagnostic.ParseHTTPFormOptions(r)
// audit logs // audit logs
log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": common.CallerName(0), "url": r.URL.String()}) log := logrus.WithFields(logrus.Fields{"component": "diagnostic", "remoteIP": r.RemoteAddr, "method": caller.Name(0), "url": r.URL.String()})
log.Info("network stats") log.Info("network stats")
if len(r.Form["nid"]) < 1 { if len(r.Form["nid"]) < 1 {

View file

@ -289,6 +289,16 @@ func (n *networkNamespace) AddInterface(srcName, dstPrefix string, options ...If
// Configure the interface now this is moved in the proper namespace. // Configure the interface now this is moved in the proper namespace.
if err := configureInterface(nlh, iface, i); err != nil { if err := configureInterface(nlh, iface, i); err != nil {
// If configuring the device fails move it back to the host namespace
// and change the name back to the source name. This allows the caller
// to properly cleanup the interface. Its important especially for
// interfaces with global attributes, ex: vni id for vxlan interfaces.
if nerr := nlh.LinkSetName(iface, i.SrcName()); nerr != nil {
logrus.Errorf("renaming interface (%s->%s) failed, %v after config error %v", i.DstName(), i.SrcName(), nerr, err)
}
if nerr := nlh.LinkSetNsFd(iface, ns.ParseHandlerInt()); nerr != nil {
logrus.Errorf("moving inteface %s to host ns failed, %v, after config error %v", i.SrcName(), nerr, err)
}
return err return err
} }

View file

@ -2,7 +2,7 @@ package kernel
type conditionalCheck func(val1, val2 string) bool type conditionalCheck func(val1, val2 string) bool
// OSValue represents a tuple, value defired, check function when to apply the value // OSValue represents a tuple, value defined, check function when to apply the value
type OSValue struct { type OSValue struct {
Value string Value string
CheckFn conditionalCheck CheckFn conditionalCheck

View file

@ -14,6 +14,11 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const (
// DefaultResolvConf points to the default file used for dns configuration on a linux machine
DefaultResolvConf = "/etc/resolv.conf"
)
var ( var (
// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS // Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS
defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"} defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"}
@ -50,15 +55,7 @@ type File struct {
// Get returns the contents of /etc/resolv.conf and its hash // Get returns the contents of /etc/resolv.conf and its hash
func Get() (*File, error) { func Get() (*File, error) {
resolv, err := ioutil.ReadFile("/etc/resolv.conf") return GetSpecific(DefaultResolvConf)
if err != nil {
return nil, err
}
hash, err := ioutils.HashData(bytes.NewReader(resolv))
if err != nil {
return nil, err
}
return &File{Content: resolv, Hash: hash}, nil
} }
// GetSpecific returns the contents of the user specified resolv.conf file and its hash // GetSpecific returns the contents of the user specified resolv.conf file and its hash

View file

@ -35,7 +35,7 @@ type Resolver interface {
} }
// DNSBackend represents a backend DNS resolver used for DNS name // DNSBackend represents a backend DNS resolver used for DNS name
// resolution. All the queries to the resolver are forwared to the // resolution. All the queries to the resolver are forwarded to the
// backend resolver. // backend resolver.
type DNSBackend interface { type DNSBackend interface {
// ResolveName resolves a service name to an IPv4 or IPv6 address by searching // ResolveName resolves a service name to an IPv4 or IPv6 address by searching

View file

@ -84,6 +84,7 @@ type sandbox struct {
ingress bool ingress bool
ndotsSet bool ndotsSet bool
oslTypes []osl.SandboxType // slice of properties of this sandbox oslTypes []osl.SandboxType // slice of properties of this sandbox
loadBalancerNID string // NID that this SB is a load balancer for
sync.Mutex sync.Mutex
// This mutex is used to serialize service related operation for an endpoint // This mutex is used to serialize service related operation for an endpoint
// The lock is here because the endpoint is saved into the store so is not unique // The lock is here because the endpoint is saved into the store so is not unique
@ -467,7 +468,7 @@ func (sb *sandbox) ResolveService(name string) ([]*net.SRV, []net.IP) {
logrus.Debugf("Service name To resolve: %v", name) logrus.Debugf("Service name To resolve: %v", name)
// There are DNS implementaions that allow SRV queries for names not in // There are DNS implementations that allow SRV queries for names not in
// the format defined by RFC 2782. Hence specific validations checks are // the format defined by RFC 2782. Hence specific validations checks are
// not done // not done
parts := strings.Split(name, ".") parts := strings.Split(name, ".")
@ -1098,8 +1099,8 @@ func OptionDNSOptions(options string) SandboxOption {
} }
} }
// OptionUseDefaultSandbox function returns an option setter for using default sandbox to // OptionUseDefaultSandbox function returns an option setter for using default sandbox
// be passed to container Create method. // (host namespace) to be passed to container Create method.
func OptionUseDefaultSandbox() SandboxOption { func OptionUseDefaultSandbox() SandboxOption {
return func(sb *sandbox) { return func(sb *sandbox) {
sb.config.useDefaultSandBox = true sb.config.useDefaultSandBox = true
@ -1169,8 +1170,9 @@ func OptionIngress() SandboxOption {
// OptionLoadBalancer function returns an option setter for marking a // OptionLoadBalancer function returns an option setter for marking a
// sandbox as a load balancer sandbox. // sandbox as a load balancer sandbox.
func OptionLoadBalancer() SandboxOption { func OptionLoadBalancer(nid string) SandboxOption {
return func(sb *sandbox) { return func(sb *sandbox) {
sb.loadBalancerNID = nid
sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeLoadBalancer) sb.oslTypes = append(sb.oslTypes, osl.SandboxTypeLoadBalancer)
} }
} }

View file

@ -81,7 +81,9 @@ func (sb *sandbox) buildHostsFile() error {
} }
// This is for the host mode networking // This is for the host mode networking
if sb.config.originHostsPath != "" { if sb.config.useDefaultSandBox && len(sb.config.extraHosts) == 0 {
// We are working under the assumption that the origin file option had been properly expressed by the upper layer
// if not here we are going to error out
if err := copyFile(sb.config.originHostsPath, sb.config.hostsPath); err != nil && !os.IsNotExist(err) { if err := copyFile(sb.config.originHostsPath, sb.config.hostsPath); err != nil && !os.IsNotExist(err) {
return types.InternalErrorf("could not copy source hosts file %s to %s: %v", sb.config.originHostsPath, sb.config.hostsPath, err) return types.InternalErrorf("could not copy source hosts file %s to %s: %v", sb.config.originHostsPath, sb.config.hostsPath, err)
} }
@ -190,8 +192,13 @@ func (sb *sandbox) setupDNS() error {
return err return err
} }
// This is for the host mode networking // When the user specify a conainter in the host namespace and do no have any dns option specified
if sb.config.originResolvConfPath != "" { // we just copy the host resolv.conf from the host itself
if sb.config.useDefaultSandBox &&
len(sb.config.dnsList) == 0 && len(sb.config.dnsSearchList) == 0 && len(sb.config.dnsOptionsList) == 0 {
// We are working under the assumption that the origin file option had been properly expressed by the upper layer
// if not here we are going to error out
if err := copyFile(sb.config.originResolvConfPath, sb.config.resolvConfPath); err != nil { if err := copyFile(sb.config.originResolvConfPath, sb.config.resolvConfPath); err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", sb.config.originResolvConfPath, sb.config.resolvConfPath, err) return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", sb.config.originResolvConfPath, sb.config.resolvConfPath, err)
@ -204,7 +211,12 @@ func (sb *sandbox) setupDNS() error {
return nil return nil
} }
currRC, err := resolvconf.Get() originResolvConfPath := sb.config.originResolvConfPath
if originResolvConfPath == "" {
// if not specified fallback to default /etc/resolv.conf
originResolvConfPath = resolvconf.DefaultResolvConf
}
currRC, err := resolvconf.GetSpecific(originResolvConfPath)
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
return err return err
@ -241,7 +253,7 @@ func (sb *sandbox) setupDNS() error {
sb.setExternalResolvers(newRC.Content, types.IPv4, false) sb.setExternalResolvers(newRC.Content, types.IPv4, false)
} else { } else {
// If the host resolv.conf file has 127.0.0.x container should // If the host resolv.conf file has 127.0.0.x container should
// use the host restolver for queries. This is supported by the // use the host resolver for queries. This is supported by the
// docker embedded DNS server. Hence save the external resolvers // docker embedded DNS server. Hence save the external resolvers
// before filtering it out. // before filtering it out.
sb.setExternalResolvers(currRC.Content, types.IPv4, true) sb.setExternalResolvers(currRC.Content, types.IPv4, true)
@ -271,7 +283,7 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
) )
// This is for the host mode networking // This is for the host mode networking
if sb.config.originResolvConfPath != "" { if sb.config.useDefaultSandBox {
return nil return nil
} }

View file

@ -5,7 +5,7 @@ import (
"net" "net"
"sync" "sync"
"github.com/docker/libnetwork/common" "github.com/docker/libnetwork/internal/setmatrix"
) )
var ( var (
@ -54,7 +54,7 @@ type service struct {
// associated with it. At stable state the endpoint ID expected is 1 // associated with it. At stable state the endpoint ID expected is 1
// but during transition and service change it is possible to have // but during transition and service change it is possible to have
// temporary more than 1 // temporary more than 1
ipToEndpoint common.SetMatrix ipToEndpoint setmatrix.SetMatrix
deleted bool deleted bool

View file

@ -5,7 +5,7 @@ package libnetwork
import ( import (
"net" "net"
"github.com/docker/libnetwork/common" "github.com/docker/libnetwork/internal/setmatrix"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -139,7 +139,7 @@ func newService(name string, id string, ingressPorts []*PortConfig, serviceAlias
ingressPorts: ingressPorts, ingressPorts: ingressPorts,
loadBalancers: make(map[string]*loadBalancer), loadBalancers: make(map[string]*loadBalancer),
aliases: serviceAliases, aliases: serviceAliases,
ipToEndpoint: common.NewSetMatrix(), ipToEndpoint: setmatrix.NewSetMatrix(),
} }
} }

View file

@ -27,7 +27,7 @@ import (
func init() { func init() {
reexec.Register("fwmarker", fwMarker) reexec.Register("fwmarker", fwMarker)
reexec.Register("redirecter", redirecter) reexec.Register("redirector", redirector)
} }
// Populate all loadbalancers on the network that the passed endpoint // Populate all loadbalancers on the network that the passed endpoint
@ -431,7 +431,7 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
// DOCKER-USER so the user is able to filter packet first. // DOCKER-USER so the user is able to filter packet first.
// The second rule should be jump to INGRESS-CHAIN. // The second rule should be jump to INGRESS-CHAIN.
// This chain has the rules to allow access to the published ports for swarm tasks // This chain has the rules to allow access to the published ports for swarm tasks
// from local bridge networks and docker_gwbridge (ie:taks on other swarm netwroks) // from local bridge networks and docker_gwbridge (ie:taks on other swarm networks)
func arrangeIngressFilterRule() { func arrangeIngressFilterRule() {
if iptables.ExistChain(ingressChain, iptables.Filter) { if iptables.ExistChain(ingressChain, iptables.Filter) {
if iptables.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) { if iptables.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
@ -668,7 +668,7 @@ func addRedirectRules(path string, eIP *net.IPNet, ingressPorts []*PortConfig) e
cmd := &exec.Cmd{ cmd := &exec.Cmd{
Path: reexec.Self(), Path: reexec.Self(),
Args: append([]string{"redirecter"}, path, eIP.String(), ingressPortsFile), Args: append([]string{"redirector"}, path, eIP.String(), ingressPortsFile),
Stdout: os.Stdout, Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
} }
@ -680,8 +680,8 @@ func addRedirectRules(path string, eIP *net.IPNet, ingressPorts []*PortConfig) e
return nil return nil
} }
// Redirecter reexec function. // Redirector reexec function.
func redirecter() { func redirector() {
runtime.LockOSThread() runtime.LockOSThread()
defer runtime.UnlockOSThread() defer runtime.UnlockOSThread()