зеркало из https://github.com/microsoft/docker.git
Merge pull request #20419 from aboch/cr
Allow passing global datastore to libnetwork and v0.7.0-dev1 vendoring
This commit is contained in:
Коммит
117a982d2e
|
@ -1598,12 +1598,12 @@ func (daemon *Daemon) initDiscovery(config *Config) error {
|
|||
// daemon according to those changes.
|
||||
// This are the settings that Reload changes:
|
||||
// - Daemon labels.
|
||||
// - Cluster discovery (reconfigure and restart).
|
||||
func (daemon *Daemon) Reload(config *Config) error {
|
||||
daemon.configStore.reloadLock.Lock()
|
||||
defer daemon.configStore.reloadLock.Unlock()
|
||||
daemon.configStore.Labels = config.Labels
|
||||
daemon.configStore.reloadLock.Unlock()
|
||||
|
||||
return nil
|
||||
return daemon.reloadClusterDiscovery(config)
|
||||
}
|
||||
|
||||
func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {
|
||||
|
@ -1640,6 +1640,19 @@ func (daemon *Daemon) reloadClusterDiscovery(config *Config) error {
|
|||
daemon.configStore.ClusterOpts = config.ClusterOpts
|
||||
daemon.configStore.ClusterAdvertise = newAdvertise
|
||||
|
||||
if daemon.netController == nil {
|
||||
return nil
|
||||
}
|
||||
netOptions, err := daemon.networkOptions(daemon.configStore)
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed to reload configuration with network controller: %v", err)
|
||||
return nil
|
||||
}
|
||||
err = daemon.netController.ReloadConfiguration(netOptions...)
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed to reload configuration with network controller: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -371,7 +371,7 @@ func TestDaemonDiscoveryReload(t *testing.T) {
|
|||
&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
|
||||
}
|
||||
|
||||
if err := daemon.reloadClusterDiscovery(newConfig); err != nil {
|
||||
if err := daemon.Reload(newConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ch, errCh = daemon.discoveryWatcher.Watch(stopCh)
|
||||
|
@ -403,7 +403,7 @@ func TestDaemonDiscoveryReloadFromEmptyDiscovery(t *testing.T) {
|
|||
&discovery.Entry{Host: "127.0.0.1", Port: "5555"},
|
||||
}
|
||||
|
||||
if err := daemon.reloadClusterDiscovery(newConfig); err != nil {
|
||||
if err := daemon.Reload(newConfig); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
stopCh := make(chan struct{})
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/libnetwork"
|
||||
nwconfig "github.com/docker/libnetwork/config"
|
||||
blkiodev "github.com/opencontainers/runc/libcontainer/configs"
|
||||
)
|
||||
|
||||
|
@ -251,3 +252,7 @@ func restoreCustomImage(is image.Store, ls layer.Store, rs reference.Store) erro
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) networkOptions(dconfig *Config) ([]nwconfig.Option, error) {
|
||||
return nil, fmt.Errorf("Network controller config reload not aavailable on Windows yet")
|
||||
}
|
||||
|
|
|
@ -890,4 +890,13 @@ if there are conflicts, but it won't stop execution.
|
|||
The list of currently supported options that can be reconfigured is this:
|
||||
|
||||
- `debug`: it changes the daemon to debug mode when set to true.
|
||||
- `cluster-store`: it reloads the discovery store with the new address.
|
||||
- `cluster-store-opts`: it uses the new options to reload the discovery store.
|
||||
- `cluster-advertise`: it modifies the address advertised after reloading.
|
||||
- `labels`: it replaces the daemon labels with a new set of labels.
|
||||
|
||||
Updating and reloading the cluster configurations such as `--cluster-store`,
|
||||
`--cluster-advertise` and `--cluster-store-opts` will take effect only if
|
||||
these configurations were not previously configured. Configuration reload will
|
||||
log a warning message if it detects a change in previously configured cluster
|
||||
configurations.
|
|
@ -29,7 +29,7 @@ clone git github.com/RackSec/srslog 6eb773f331e46fbba8eecb8e794e635e75fc04de
|
|||
clone git github.com/imdario/mergo 0.2.1
|
||||
|
||||
#get libnetwork packages
|
||||
clone git github.com/docker/libnetwork v0.7.0-dev.2
|
||||
clone git github.com/docker/libnetwork v0.7.0-dev.3
|
||||
clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
|
||||
clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
|
||||
clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/integration/checker"
|
||||
|
@ -2156,3 +2157,43 @@ func (s *DockerDaemonSuite) TestDaemonDebugLog(c *check.C) {
|
|||
newD.Stop()
|
||||
c.Assert(b.String(), checker.Contains, debugLog)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDaemonDiscoveryBackendConfigReload(c *check.C) {
|
||||
testRequires(c, SameHostDaemon, DaemonIsLinux)
|
||||
|
||||
// daemon config file
|
||||
daemonConfig := `{ "debug" : false }`
|
||||
configFilePath := "test.json"
|
||||
|
||||
configFile, err := os.Create(configFilePath)
|
||||
c.Assert(err, checker.IsNil)
|
||||
fmt.Fprintf(configFile, "%s", daemonConfig)
|
||||
|
||||
d := NewDaemon(c)
|
||||
err = d.Start(fmt.Sprintf("--config-file=%s", configFilePath))
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer d.Stop()
|
||||
|
||||
// daemon config file
|
||||
daemonConfig = `{
|
||||
"cluster-store": "consul://consuladdr:consulport/some/path",
|
||||
"cluster-advertise": "192.168.56.100:0",
|
||||
"debug" : false
|
||||
}`
|
||||
|
||||
configFile.Close()
|
||||
os.Remove(configFilePath)
|
||||
|
||||
configFile, err = os.Create(configFilePath)
|
||||
c.Assert(err, checker.IsNil)
|
||||
fmt.Fprintf(configFile, "%s", daemonConfig)
|
||||
|
||||
syscall.Kill(d.cmd.Process.Pid, syscall.SIGHUP)
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
out, err := d.Cmd("info")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster store: consul://consuladdr:consulport/some/path"))
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("Cluster advertise: 192.168.56.100:0"))
|
||||
}
|
||||
|
|
|
@ -1389,6 +1389,14 @@ func (s *DockerSuite) TestUserDefinedNetworkConnectivity(c *check.C) {
|
|||
c.Assert(err, check.NotNil)
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestEmbeddedDNSInvalidInput(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux, NotUserNamespace)
|
||||
dockerCmd(c, "network", "create", "-d", "bridge", "nw1")
|
||||
|
||||
// Sending garbge to embedded DNS shouldn't crash the daemon
|
||||
dockerCmd(c, "run", "-i", "--net=nw1", "--name=c1", "debian:jessie", "bash", "-c", "echo InvalidQuery > /dev/udp/127.0.0.11/53")
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestDockerNetworkConnectFailsNoInspectChange(c *check.C) {
|
||||
dockerCmd(c, "run", "-d", "--name=bb", "busybox", "top")
|
||||
c.Assert(waitRun("bb"), check.IsNil)
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
# Changelog
|
||||
|
||||
## 0.7.0-dev.3 (2016-02-17)
|
||||
- Fixes https://github.com/docker/docker/issues/20350
|
||||
- Fixes https://github.com/docker/docker/issues/20145
|
||||
- Initial Windows HNS integration
|
||||
- Allow passing global datastore config to libnetwork after boot
|
||||
- Set Recursion Available bit in DNS query responses
|
||||
- Make sure iptables chains are recreated on firewalld reload
|
||||
|
||||
## 0.7.0-dev.2 (2016-02-11)
|
||||
- Fixes https://github.com/docker/docker/issues/20140
|
||||
|
||||
|
|
|
@ -8,6 +8,5 @@ RUN cd /go/src && mkdir -p golang.org/x && \
|
|||
RUN go get github.com/tools/godep \
|
||||
github.com/golang/lint/golint \
|
||||
golang.org/x/tools/cmd/vet \
|
||||
golang.org/x/tools/cmd/goimports \
|
||||
golang.org/x/tools/cmd/cover\
|
||||
github.com/mattn/goveralls
|
||||
|
|
|
@ -53,7 +53,7 @@ check-code:
|
|||
|
||||
check-format:
|
||||
@echo "Checking format... "
|
||||
test -z "$$(goimports -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
||||
test -z "$$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
|
||||
@echo "Done checking format"
|
||||
|
||||
run-tests:
|
||||
|
|
|
@ -61,6 +61,22 @@ func ParseConfig(tomlCfgFile string) (*Config, error) {
|
|||
return cfg, nil
|
||||
}
|
||||
|
||||
// ParseConfigOptions parses the configuration options and returns
|
||||
// a reference to the corresponding Config structure
|
||||
func ParseConfigOptions(cfgOptions ...Option) *Config {
|
||||
cfg := &Config{
|
||||
Daemon: DaemonCfg{
|
||||
DriverCfg: make(map[string]interface{}),
|
||||
},
|
||||
Scopes: make(map[string]*datastore.ScopeCfg),
|
||||
}
|
||||
|
||||
cfg.ProcessOptions(cfgOptions...)
|
||||
cfg.LoadDefaultScopes(cfg.Daemon.DataDir)
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Option is an option setter function type used to pass various configurations
|
||||
// to the controller
|
||||
type Option func(c *Config)
|
||||
|
|
|
@ -106,6 +106,9 @@ type NetworkController interface {
|
|||
|
||||
// Stop network controller
|
||||
Stop()
|
||||
|
||||
// ReloadCondfiguration updates the controller configuration
|
||||
ReloadConfiguration(cfgOptions ...config.Option) error
|
||||
}
|
||||
|
||||
// NetworkWalker is a client provided function which will be used to walk the Networks.
|
||||
|
@ -129,7 +132,6 @@ type ipamData struct {
|
|||
}
|
||||
|
||||
type driverTable map[string]*driverData
|
||||
|
||||
type ipamTable map[string]*ipamData
|
||||
type sandboxTable map[string]*sandbox
|
||||
|
||||
|
@ -153,22 +155,9 @@ type controller struct {
|
|||
|
||||
// New creates a new instance of network controller.
|
||||
func New(cfgOptions ...config.Option) (NetworkController, error) {
|
||||
var cfg *config.Config
|
||||
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,
|
||||
cfg: config.ParseConfigOptions(cfgOptions...),
|
||||
sandboxes: sandboxTable{},
|
||||
drivers: driverTable{},
|
||||
ipamDrivers: ipamTable{},
|
||||
|
@ -179,8 +168,8 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if cfg != nil && cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(cfg.Cluster.Watcher); err != nil {
|
||||
if c.cfg != nil && c.cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil {
|
||||
// Failing to initalize discovery is a bad situation to be in.
|
||||
// But it cannot fail creating the Controller
|
||||
log.Errorf("Failed to Initialize Discovery : %v", err)
|
||||
|
@ -206,6 +195,83 @@ func New(cfgOptions ...config.Option) (NetworkController, error) {
|
|||
return c, nil
|
||||
}
|
||||
|
||||
var procReloadConfig = make(chan (bool), 1)
|
||||
|
||||
func (c *controller) ReloadConfiguration(cfgOptions ...config.Option) error {
|
||||
procReloadConfig <- true
|
||||
defer func() { <-procReloadConfig }()
|
||||
|
||||
// For now we accept the configuration reload only as a mean to provide a global store config after boot.
|
||||
// Refuse the configuration if it alters an existing datastore client configuration.
|
||||
update := false
|
||||
cfg := config.ParseConfigOptions(cfgOptions...)
|
||||
for s := range c.cfg.Scopes {
|
||||
if _, ok := cfg.Scopes[s]; !ok {
|
||||
return types.ForbiddenErrorf("cannot accept new configuration because it removes an existing datastore client")
|
||||
}
|
||||
}
|
||||
for s, nSCfg := range cfg.Scopes {
|
||||
if eSCfg, ok := c.cfg.Scopes[s]; ok {
|
||||
if eSCfg.Client.Provider != nSCfg.Client.Provider ||
|
||||
eSCfg.Client.Address != nSCfg.Client.Address {
|
||||
return types.ForbiddenErrorf("cannot accept new configuration because it modifies an existing datastore client")
|
||||
}
|
||||
} else {
|
||||
update = true
|
||||
}
|
||||
}
|
||||
if !update {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Lock()
|
||||
c.cfg = cfg
|
||||
c.Unlock()
|
||||
|
||||
if err := c.initStores(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.discovery == nil && c.cfg.Cluster.Watcher != nil {
|
||||
if err := c.initDiscovery(c.cfg.Cluster.Watcher); err != nil {
|
||||
log.Errorf("Failed to Initialize Discovery after configuration update: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
var dsConfig *discoverapi.DatastoreConfigData
|
||||
for scope, sCfg := range cfg.Scopes {
|
||||
if scope == datastore.LocalScope || !sCfg.IsValid() {
|
||||
continue
|
||||
}
|
||||
dsConfig = &discoverapi.DatastoreConfigData{
|
||||
Scope: scope,
|
||||
Provider: sCfg.Client.Provider,
|
||||
Address: sCfg.Client.Address,
|
||||
Config: sCfg.Client.Config,
|
||||
}
|
||||
break
|
||||
}
|
||||
if dsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for nm, id := range c.getIpamDrivers() {
|
||||
err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", nm, err)
|
||||
}
|
||||
}
|
||||
|
||||
for nm, id := range c.getNetDrivers() {
|
||||
err := id.driver.DiscoverNew(discoverapi.DatastoreConfig, *dsConfig)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to set datastore in driver %s: %v", nm, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controller) ID() string {
|
||||
return c.id
|
||||
}
|
||||
|
@ -726,6 +792,26 @@ func (c *controller) getIpamDriver(name string) (ipamapi.Ipam, error) {
|
|||
return id.driver, nil
|
||||
}
|
||||
|
||||
func (c *controller) getIpamDrivers() ipamTable {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
table := ipamTable{}
|
||||
for i, d := range c.ipamDrivers {
|
||||
table[i] = d
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func (c *controller) getNetDrivers() driverTable {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
table := driverTable{}
|
||||
for i, d := range c.drivers {
|
||||
table[i] = d
|
||||
}
|
||||
return table
|
||||
}
|
||||
|
||||
func (c *controller) Stop() {
|
||||
c.closeStores()
|
||||
c.stopExternalKeyListener()
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/docker/libkv/store/consul"
|
||||
"github.com/docker/libkv/store/etcd"
|
||||
"github.com/docker/libkv/store/zookeeper"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
|
@ -253,6 +254,34 @@ func NewDataStore(scope string, cfg *ScopeCfg) (DataStore, error) {
|
|||
return newClient(scope, cfg.Client.Provider, cfg.Client.Address, cfg.Client.Config, cached)
|
||||
}
|
||||
|
||||
// NewDataStoreFromConfig creates a new instance of LibKV data store starting from the datastore config data
|
||||
func NewDataStoreFromConfig(dsc discoverapi.DatastoreConfigData) (DataStore, error) {
|
||||
var (
|
||||
ok bool
|
||||
sCfgP *store.Config
|
||||
)
|
||||
|
||||
sCfgP, ok = dsc.Config.(*store.Config)
|
||||
if !ok && dsc.Config != nil {
|
||||
return nil, fmt.Errorf("cannot parse store configuration: %v", dsc.Config)
|
||||
}
|
||||
|
||||
scopeCfg := &ScopeCfg{
|
||||
Client: ScopeClientCfg{
|
||||
Address: dsc.Address,
|
||||
Provider: dsc.Provider,
|
||||
Config: sCfgP,
|
||||
},
|
||||
}
|
||||
|
||||
ds, err := NewDataStore(dsc.Scope, scopeCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to construct datastore client from datastore configuration %v: %v", dsc, err)
|
||||
}
|
||||
|
||||
return ds, err
|
||||
}
|
||||
|
||||
func (ds *datastore) Close() {
|
||||
ds.store.Close()
|
||||
}
|
||||
|
|
|
@ -16,8 +16,8 @@ type DiscoveryType int
|
|||
const (
|
||||
// NodeDiscovery represents Node join/leave events provided by discovery
|
||||
NodeDiscovery = iota + 1
|
||||
// DatastoreUpdate represents a add/remove datastore event
|
||||
DatastoreUpdate
|
||||
// DatastoreConfig represents a add/remove datastore event
|
||||
DatastoreConfig
|
||||
)
|
||||
|
||||
// NodeDiscoveryData represents the structure backing the node discovery data json string
|
||||
|
@ -26,8 +26,9 @@ type NodeDiscoveryData struct {
|
|||
Self bool
|
||||
}
|
||||
|
||||
// DatastoreUpdateData is the data for the datastore update event message
|
||||
type DatastoreUpdateData struct {
|
||||
// DatastoreConfigData is the data for the datastore update event message
|
||||
type DatastoreConfigData struct {
|
||||
Scope string
|
||||
Provider string
|
||||
Address string
|
||||
Config interface{}
|
||||
|
|
|
@ -3,11 +3,13 @@ package libnetwork
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
|
||||
builtinIpam "github.com/docker/libnetwork/ipams/builtin"
|
||||
remoteIpam "github.com/docker/libnetwork/ipams/remote"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
)
|
||||
|
||||
type initializer struct {
|
||||
|
@ -56,10 +58,12 @@ func makeDriverConfig(c *controller, ntype string) map[string]interface{} {
|
|||
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
|
||||
config[netlabel.MakeKVClient(k)] = discoverapi.DatastoreConfigData{
|
||||
Scope: k,
|
||||
Provider: v.Client.Provider,
|
||||
Address: v.Client.Address,
|
||||
Config: v.Client.Config,
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
|
|
|
@ -133,6 +133,9 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|||
if out, err := exec.Command("modprobe", "-va", "nf_nat").CombinedOutput(); err != nil {
|
||||
logrus.Warnf("Running modprobe nf_nat failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
|
||||
}
|
||||
if out, err := exec.Command("modprobe", "-va", "xt_conntrack").CombinedOutput(); err != nil {
|
||||
logrus.Warnf("Running modprobe xt_conntrack failed with message: `%s`, error: %v", strings.TrimSpace(string(out)), err)
|
||||
}
|
||||
if err := iptables.FirewalldInit(); err != nil {
|
||||
logrus.Debugf("Fail to initialize firewalld: %v, using raw iptables instead", err)
|
||||
}
|
||||
|
@ -384,6 +387,8 @@ func (d *driver) configure(option map[string]interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Make sure on firewall reload, first thing being re-played is chains creation
|
||||
iptables.OnReloaded(func() { logrus.Debugf("Recreating iptables chains on firewall reload"); setupIPChains(config) })
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"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/discoverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
@ -16,27 +16,15 @@ import (
|
|||
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),
|
||||
},
|
||||
if data, ok := option[netlabel.LocalKVClient]; ok {
|
||||
var err error
|
||||
dsc, ok := data.(discoverapi.DatastoreConfigData)
|
||||
if !ok {
|
||||
return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
|
||||
}
|
||||
|
||||
provConfig, confOk := option[netlabel.LocalKVProviderConfig]
|
||||
if confOk {
|
||||
cfg.Client.Config = provConfig.(*store.Config)
|
||||
}
|
||||
|
||||
d.store, err = datastore.NewDataStore(datastore.LocalScope, cfg)
|
||||
d.store, err = datastore.NewDataStoreFromConfig(dsc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bridge driver failed to initialize data store: %v", err)
|
||||
return types.InternalErrorf("bridge driver failed to initialize data store: %v", err)
|
||||
}
|
||||
|
||||
return d.populateNetworks()
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/idm"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
"github.com/hashicorp/serf/serf"
|
||||
)
|
||||
|
||||
|
@ -25,6 +25,8 @@ const (
|
|||
vxlanVethMTU = 1450
|
||||
)
|
||||
|
||||
var initVxlanIdm = make(chan (bool), 1)
|
||||
|
||||
type driver struct {
|
||||
eventCh chan serf.Event
|
||||
notifyCh chan ovNotify
|
||||
|
@ -56,6 +58,18 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
|||
config: config,
|
||||
}
|
||||
|
||||
if data, ok := config[netlabel.GlobalKVClient]; ok {
|
||||
var err error
|
||||
dsc, ok := data.(discoverapi.DatastoreConfigData)
|
||||
if !ok {
|
||||
return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
|
||||
}
|
||||
d.store, err = datastore.NewDataStoreFromConfig(dsc)
|
||||
if err != nil {
|
||||
return types.InternalErrorf("failed to initialize data store: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return dc.RegisterDriver(networkType, d, c)
|
||||
}
|
||||
|
||||
|
@ -73,42 +87,33 @@ func Fini(drv driverapi.Driver) {
|
|||
}
|
||||
|
||||
func (d *driver) configure() error {
|
||||
if d.store == nil {
|
||||
return types.NoServiceErrorf("datastore is not available")
|
||||
}
|
||||
|
||||
if d.vxlanIdm == nil {
|
||||
return d.initializeVxlanIdm()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) initializeVxlanIdm() error {
|
||||
var err error
|
||||
|
||||
if len(d.config) == 0 {
|
||||
initVxlanIdm <- true
|
||||
defer func() { <-initVxlanIdm }()
|
||||
|
||||
if d.vxlanIdm != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
d.once.Do(func() {
|
||||
provider, provOk := d.config[netlabel.GlobalKVProvider]
|
||||
provURL, urlOk := d.config[netlabel.GlobalKVProviderURL]
|
||||
d.vxlanIdm, err = idm.New(d.store, "vxlan-id", vxlanIDStart, vxlanIDEnd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize vxlan id manager: %v", err)
|
||||
}
|
||||
|
||||
if provOk && urlOk {
|
||||
cfg := &datastore.ScopeCfg{
|
||||
Client: datastore.ScopeClientCfg{
|
||||
Provider: provider.(string),
|
||||
Address: provURL.(string),
|
||||
},
|
||||
}
|
||||
provConfig, confOk := d.config[netlabel.GlobalKVProviderConfig]
|
||||
if confOk {
|
||||
cfg.Client.Config = provConfig.(*store.Config)
|
||||
}
|
||||
d.store, err = datastore.NewDataStore(datastore.GlobalScope, cfg)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to initialize data store: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
d.vxlanIdm, err = idm.New(d.store, "vxlan-id", vxlanIDStart, vxlanIDEnd)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to initialize vxlan id manager: %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Type() string {
|
||||
|
@ -187,12 +192,27 @@ func (d *driver) pushLocalEndpointEvent(action, nid, eid string) {
|
|||
|
||||
// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
|
||||
func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
if dType == discoverapi.NodeDiscovery {
|
||||
switch dType {
|
||||
case discoverapi.NodeDiscovery:
|
||||
nodeData, ok := data.(discoverapi.NodeDiscoveryData)
|
||||
if !ok || nodeData.Address == "" {
|
||||
return fmt.Errorf("invalid discovery data")
|
||||
}
|
||||
d.nodeJoin(nodeData.Address, nodeData.Self)
|
||||
case discoverapi.DatastoreConfig:
|
||||
var err error
|
||||
if d.store != nil {
|
||||
return types.ForbiddenErrorf("cannot accept datastore configuration: Overlay driver has a datastore configured already")
|
||||
}
|
||||
dsc, ok := data.(discoverapi.DatastoreConfigData)
|
||||
if !ok {
|
||||
return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
|
||||
}
|
||||
d.store, err = datastore.NewDataStoreFromConfig(dsc)
|
||||
if err != nil {
|
||||
return types.InternalErrorf("failed to initialize data store: %v", err)
|
||||
}
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package windows
|
||||
|
||||
const (
|
||||
// NetworkName label for bridge driver
|
||||
NetworkName = "com.docker.network.windowsshim.networkname"
|
||||
|
||||
// HNSID of the discovered network
|
||||
HNSID = "com.docker.network.windowsshim.hnsid"
|
||||
|
||||
// RoutingDomain of the network
|
||||
RoutingDomain = "com.docker.network.windowsshim.routingdomain"
|
||||
)
|
|
@ -1,57 +1,419 @@
|
|||
// +build windows
|
||||
|
||||
// Shim for the Host Network Service (HNS) to manage networking for
|
||||
// Windows Server containers and Hyper-V containers. This module
|
||||
// is a basic libnetwork driver that passes all the calls to HNS
|
||||
// It implements the 4 networking modes supported by HNS L2Bridge,
|
||||
// L2Tunnel, NAT and Transparent(DHCP)
|
||||
//
|
||||
// The network are stored in memory and docker daemon ensures discovering
|
||||
// and loading these networks on startup
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/driverapi"
|
||||
"github.com/docker/libnetwork/netlabel"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const networkType = "windows"
|
||||
|
||||
// TODO Windows. This is a placeholder for now
|
||||
|
||||
type driver struct{}
|
||||
|
||||
// Init registers a new instance of null driver
|
||||
func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
||||
c := driverapi.Capability{
|
||||
DataScope: datastore.LocalScope,
|
||||
}
|
||||
return dc.RegisterDriver(networkType, &driver{}, c)
|
||||
// networkConfiguration for network specific configuration
|
||||
type networkConfiguration struct {
|
||||
ID string
|
||||
Type string
|
||||
Name string
|
||||
HnsID string
|
||||
RDID string
|
||||
}
|
||||
|
||||
type hnsEndpoint struct {
|
||||
id string
|
||||
profileID string
|
||||
macAddress net.HardwareAddr
|
||||
addr *net.IPNet
|
||||
}
|
||||
|
||||
type hnsNetwork struct {
|
||||
id string
|
||||
config *networkConfiguration
|
||||
endpoints map[string]*hnsEndpoint // key: endpoint id
|
||||
driver *driver // The network's driver
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type driver struct {
|
||||
name string
|
||||
networks map[string]*hnsNetwork
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func isValidNetworkType(networkType string) bool {
|
||||
if "L2Bridge" == networkType || "L2Tunnel" == networkType || "NAT" == networkType || "Transparent" == networkType {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// New constructs a new bridge driver
|
||||
func newDriver(networkType string) *driver {
|
||||
return &driver{name: networkType, networks: map[string]*hnsNetwork{}}
|
||||
}
|
||||
|
||||
// GetInit returns an initializer for the given network type
|
||||
func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
||||
return func(dc driverapi.DriverCallback, config map[string]interface{}) error {
|
||||
if !isValidNetworkType(networkType) {
|
||||
return types.BadRequestErrorf("Network type not supported: %s", networkType)
|
||||
}
|
||||
|
||||
return dc.RegisterDriver(networkType, newDriver(networkType), driverapi.Capability{
|
||||
DataScope: datastore.LocalScope,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (d *driver) getNetwork(id string) (*hnsNetwork, error) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
if nw, ok := d.networks[id]; ok {
|
||||
return nw, nil
|
||||
}
|
||||
|
||||
return nil, types.NotFoundErrorf("network not found: %s", id)
|
||||
}
|
||||
|
||||
func (n *hnsNetwork) getEndpoint(eid string) (*hnsEndpoint, error) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if ep, ok := n.endpoints[eid]; ok {
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
return nil, types.NotFoundErrorf("Endpoint not found: %s", eid)
|
||||
}
|
||||
|
||||
func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string) (*networkConfiguration, error) {
|
||||
config := &networkConfiguration{}
|
||||
|
||||
for label, value := range genericOptions {
|
||||
switch label {
|
||||
case NetworkName:
|
||||
config.Name = value
|
||||
case HNSID:
|
||||
config.HnsID = value
|
||||
case RoutingDomain:
|
||||
config.RDID = value
|
||||
}
|
||||
}
|
||||
|
||||
config.ID = id
|
||||
config.Type = d.name
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []driverapi.IPAMData) error {
|
||||
if len(ipamV6Data) > 0 {
|
||||
return types.ForbiddenErrorf("windowsshim driver doesnt support v6 subnets")
|
||||
}
|
||||
|
||||
if len(ipamV4Data) == 0 {
|
||||
return types.BadRequestErrorf("network %s requires ipv4 configuration", id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a new network
|
||||
func (d *driver) CreateNetwork(id string, option map[string]interface{}, ipV4Data, ipV6Data []driverapi.IPAMData) error {
|
||||
if _, err := d.getNetwork(id); err == nil {
|
||||
return types.ForbiddenErrorf("network %s exists", id)
|
||||
}
|
||||
|
||||
genData, ok := option[netlabel.GenericData].(map[string]string)
|
||||
if !ok {
|
||||
return fmt.Errorf("Unknown generic data option")
|
||||
}
|
||||
|
||||
// Parse and validate the config. It should not conflict with existing networks' config
|
||||
config, err := d.parseNetworkOptions(id, genData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = config.processIPAM(id, ipV4Data, ipV6Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
network := &hnsNetwork{
|
||||
id: config.ID,
|
||||
endpoints: make(map[string]*hnsEndpoint),
|
||||
config: config,
|
||||
driver: d,
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
d.networks[config.ID] = network
|
||||
d.Unlock()
|
||||
|
||||
// A non blank hnsid indicates that the network was discovered
|
||||
// from HNS. No need to call HNS if this network was discovered
|
||||
// from HNS
|
||||
if config.HnsID == "" {
|
||||
subnets := []hcsshim.Subnet{}
|
||||
|
||||
for _, ipData := range ipV4Data {
|
||||
subnet := hcsshim.Subnet{
|
||||
AddressPrefix: ipData.Pool.String(),
|
||||
GatewayAddress: ipData.Gateway.IP.String(),
|
||||
}
|
||||
|
||||
subnets = append(subnets, subnet)
|
||||
}
|
||||
|
||||
network := &hcsshim.HNSNetwork{
|
||||
Name: config.Name,
|
||||
Type: d.name,
|
||||
Subnets: subnets,
|
||||
}
|
||||
|
||||
if network.Name == "" {
|
||||
network.Name = id
|
||||
}
|
||||
|
||||
configurationb, err := json.Marshal(network)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configuration := string(configurationb)
|
||||
log.Debugf("HNSNetwork Request =%v Address Space=%v", configuration, subnets)
|
||||
|
||||
hnsresponse, err := hcsshim.HNSNetworkRequest("POST", "", configuration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.HnsID = hnsresponse.Id
|
||||
genData[HNSID] = config.HnsID
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) DeleteNetwork(nid string) error {
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return types.InternalMaskableErrorf("%s", err)
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
config := n.config
|
||||
n.Unlock()
|
||||
|
||||
// Cannot remove network if endpoints are still present
|
||||
if len(n.endpoints) != 0 {
|
||||
return fmt.Errorf("network %s has active endpoint", n.id)
|
||||
}
|
||||
|
||||
_, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Lock()
|
||||
delete(d.networks, nid)
|
||||
d.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertPortBindings(portBindings []types.PortBinding) ([]json.RawMessage, error) {
|
||||
var pbs []json.RawMessage
|
||||
|
||||
// Enumerate through the port bindings specified by the user and convert
|
||||
// them into the internal structure matching the JSON blob that can be
|
||||
// understood by the HCS.
|
||||
for _, elem := range portBindings {
|
||||
proto := strings.ToUpper(elem.Proto.String())
|
||||
if proto != "TCP" && proto != "UDP" {
|
||||
return nil, fmt.Errorf("invalid protocol %s", elem.Proto.String())
|
||||
}
|
||||
|
||||
if elem.HostPort != elem.HostPortEnd {
|
||||
return nil, fmt.Errorf("Windows does not support more than one host port in NAT settings")
|
||||
}
|
||||
|
||||
if len(elem.HostIP) != 0 {
|
||||
return nil, fmt.Errorf("Windows does not support host IP addresses in NAT settings")
|
||||
}
|
||||
|
||||
encodedPolicy, err := json.Marshal(hcsshim.NatPolicy{
|
||||
Type: "NAT",
|
||||
ExternalPort: elem.HostPort,
|
||||
InternalPort: elem.Port,
|
||||
Protocol: elem.Proto.String(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbs = append(pbs, encodedPolicy)
|
||||
}
|
||||
return pbs, nil
|
||||
}
|
||||
|
||||
func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if endpoint id is good and retrieve corresponding endpoint
|
||||
ep, err := n.getEndpoint(eid)
|
||||
if err == nil && ep != nil {
|
||||
return driverapi.ErrEndpointExists(eid)
|
||||
}
|
||||
|
||||
endpointStruct := &hcsshim.HNSEndpoint{
|
||||
VirtualNetwork: n.config.HnsID,
|
||||
}
|
||||
|
||||
// Convert the port mapping for the network
|
||||
if opt, ok := epOptions[netlabel.PortMap]; ok {
|
||||
if bs, ok := opt.([]types.PortBinding); ok {
|
||||
endpointStruct.Policies, err = convertPortBindings(bs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Invalid endpoint configuration for endpoint id%s", eid)
|
||||
}
|
||||
}
|
||||
|
||||
configurationb, err := json.Marshal(endpointStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hnsresponse, err := hcsshim.HNSEndpointRequest("POST", "", string(configurationb))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mac, err := net.ParseMAC(hnsresponse.MacAddress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO For now the ip mask is not in the info generated by HNS
|
||||
endpoint := &hnsEndpoint{
|
||||
id: eid,
|
||||
addr: &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()},
|
||||
macAddress: mac,
|
||||
}
|
||||
endpoint.profileID = hnsresponse.Id
|
||||
n.Lock()
|
||||
n.endpoints[eid] = endpoint
|
||||
n.Unlock()
|
||||
|
||||
ifInfo.SetIPAddress(endpoint.addr)
|
||||
ifInfo.SetMacAddress(endpoint.macAddress)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) DeleteEndpoint(nid, eid string) error {
|
||||
n, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return types.InternalMaskableErrorf("%s", err)
|
||||
}
|
||||
|
||||
ep, err := n.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Lock()
|
||||
delete(n.endpoints, eid)
|
||||
n.Unlock()
|
||||
|
||||
_, err = hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
|
||||
return make(map[string]interface{}, 0), nil
|
||||
network, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoint, err := network.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{}, 1)
|
||||
data["hnsid"] = endpoint.profileID
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Join method is invoked when a Sandbox is attached to an endpoint.
|
||||
func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
|
||||
network, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure that the endpoint exists
|
||||
_, err = network.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is just a stub for now
|
||||
|
||||
jinfo.DisableGatewayService()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Leave method is invoked when a Sandbox detaches from an endpoint.
|
||||
func (d *driver) Leave(nid, eid string) error {
|
||||
network, err := d.getNetwork(nid)
|
||||
if err != nil {
|
||||
return types.InternalMaskableErrorf("%s", err)
|
||||
}
|
||||
|
||||
// Ensure that the endpoint exists
|
||||
_, err = network.getEndpoint(eid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is just a stub for now
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *driver) Type() string {
|
||||
return networkType
|
||||
return d.name
|
||||
}
|
||||
|
||||
// DiscoverNew is a notification for a new discovery event, such as a new node joining a cluster
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
package libnetwork
|
||||
|
||||
import "github.com/docker/libnetwork/drivers/windows"
|
||||
import (
|
||||
"github.com/docker/libnetwork/drivers/null"
|
||||
"github.com/docker/libnetwork/drivers/windows"
|
||||
)
|
||||
|
||||
func getInitializers() []initializer {
|
||||
return []initializer{
|
||||
{windows.Init, "windows"},
|
||||
{null.Init, "null"},
|
||||
{windows.GetInit("Transparent"), "Transparent"},
|
||||
{windows.GetInit("L2Bridge"), "L2Bridge"},
|
||||
{windows.GetInit("L2Tunnel"), "L2Tunnel"},
|
||||
{windows.GetInit("NAT"), "NAT"},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/bitseq"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/ipamutils"
|
||||
"github.com/docker/libnetwork/types"
|
||||
|
@ -60,19 +61,9 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
|
|||
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,
|
||||
}
|
||||
a.initializeAddressSpace(aspc.as, aspc.ds)
|
||||
}
|
||||
|
||||
a.checkConsistency(localAddressSpace)
|
||||
a.checkConsistency(globalAddressSpace)
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
|
@ -118,25 +109,83 @@ func (a *Allocator) updateBitMasks(aSpace *addrSpace) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Checks for and fixes damaged bitmask. Meant to be called in constructor only.
|
||||
// Checks for and fixes damaged bitmask.
|
||||
func (a *Allocator) checkConsistency(as string) {
|
||||
var sKeyList []SubnetKey
|
||||
|
||||
// Retrieve this address space's configuration and bitmasks from the datastore
|
||||
a.refresh(as)
|
||||
a.Lock()
|
||||
aSpace, ok := a.addrSpaces[as]
|
||||
a.Unlock()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
a.updateBitMasks(aSpace)
|
||||
|
||||
aSpace.Lock()
|
||||
for sk, pd := range aSpace.subnets {
|
||||
if pd.Range != nil {
|
||||
continue
|
||||
}
|
||||
if err := a.addresses[sk].CheckConsistency(); err != nil {
|
||||
sKeyList = append(sKeyList, sk)
|
||||
}
|
||||
aSpace.Unlock()
|
||||
|
||||
for _, sk := range sKeyList {
|
||||
a.Lock()
|
||||
bm := a.addresses[sk]
|
||||
a.Unlock()
|
||||
if err := bm.CheckConsistency(); err != nil {
|
||||
log.Warnf("Error while running consistency check for %s: %v", sk, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Allocator) initializeAddressSpace(as string, ds datastore.DataStore) error {
|
||||
a.Lock()
|
||||
if _, ok := a.addrSpaces[as]; ok {
|
||||
a.Unlock()
|
||||
return types.ForbiddenErrorf("tried to add an axisting address space: %s", as)
|
||||
}
|
||||
a.addrSpaces[as] = &addrSpace{
|
||||
subnets: map[SubnetKey]*PoolData{},
|
||||
id: dsConfigKey + "/" + as,
|
||||
scope: ds.Scope(),
|
||||
ds: ds,
|
||||
alloc: a,
|
||||
}
|
||||
a.Unlock()
|
||||
|
||||
a.checkConsistency(as)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverNew informs the allocator about a new global scope datastore
|
||||
func (a *Allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
if dType != discoverapi.DatastoreConfig {
|
||||
return nil
|
||||
}
|
||||
|
||||
dsc, ok := data.(discoverapi.DatastoreConfigData)
|
||||
if !ok {
|
||||
return types.InternalErrorf("incorrect data in datastore update notification: %v", data)
|
||||
}
|
||||
|
||||
ds, err := datastore.NewDataStoreFromConfig(dsc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return a.initializeAddressSpace(globalAddressSpace, ds)
|
||||
}
|
||||
|
||||
// DiscoverDelete is a notification of no interest for the allocator
|
||||
func (a *Allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDefaultAddressSpaces returns the local and global default address spaces
|
||||
func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
return localAddressSpace, globalAddressSpace, nil
|
||||
|
|
|
@ -4,6 +4,7 @@ package ipamapi
|
|||
import (
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
|
@ -56,6 +57,8 @@ var (
|
|||
// Ipam represents the interface the IPAM service plugins must implement
|
||||
// in order to allow injection/modification of IPAM database.
|
||||
type Ipam interface {
|
||||
discoverapi.Discover
|
||||
|
||||
// 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
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// +build linux freebsd
|
||||
|
||||
package builtin
|
||||
|
||||
import (
|
16
vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin_windows.go
поставляемый
Normal file
16
vendor/src/github.com/docker/libnetwork/ipams/builtin/builtin_windows.go
поставляемый
Normal file
|
@ -0,0 +1,16 @@
|
|||
// +build windows
|
||||
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
|
||||
windowsipam "github.com/docker/libnetwork/ipams/windowsipam"
|
||||
)
|
||||
|
||||
// Init registers the built-in ipam service with libnetwork
|
||||
func Init(ic ipamapi.Callback, l, g interface{}) error {
|
||||
initFunc := windowsipam.GetInit(ipamapi.DefaultIPAM)
|
||||
|
||||
return initFunc(ic, l, g)
|
||||
}
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/pkg/plugins"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/ipams/remote/api"
|
||||
"github.com/docker/libnetwork/types"
|
||||
|
@ -124,3 +125,13 @@ func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
|
|||
res := &api.ReleaseAddressResponse{}
|
||||
return a.call("ReleaseAddress", req, res)
|
||||
}
|
||||
|
||||
// DiscoverNew is a notification for a new discovery event, such as a new global datastore
|
||||
func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverDelete is a notification for a discovery delete event, such as a node leaving a cluster
|
||||
func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
|
93
vendor/src/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go
поставляемый
Normal file
93
vendor/src/github.com/docker/libnetwork/ipams/windowsipam/windowsipam.go
поставляемый
Normal file
|
@ -0,0 +1,93 @@
|
|||
package windowsipam
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/discoverapi"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const (
|
||||
localAddressSpace = "LocalDefault"
|
||||
globalAddressSpace = "GlobalDefault"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultPool, _ = types.ParseCIDR("0.0.0.0/0")
|
||||
)
|
||||
|
||||
type allocator struct {
|
||||
}
|
||||
|
||||
// GetInit registers the built-in ipam service with libnetwork
|
||||
func GetInit(ipamName string) func(ic ipamapi.Callback, l, g interface{}) error {
|
||||
return func(ic ipamapi.Callback, l, g interface{}) error {
|
||||
return ic.RegisterIpamDriver(ipamName, &allocator{})
|
||||
}
|
||||
}
|
||||
|
||||
func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
return localAddressSpace, globalAddressSpace, nil
|
||||
}
|
||||
|
||||
// RequestPool returns an address pool along with its unique id. This is a null ipam driver. It allocates the
|
||||
// subnet user asked and does not validate anything. Doesnt support subpool allocation
|
||||
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)
|
||||
if subPool != "" || v6 {
|
||||
return "", nil, nil, types.InternalErrorf("This request is not supported by null ipam driver")
|
||||
}
|
||||
|
||||
var ipNet *net.IPNet
|
||||
var err error
|
||||
|
||||
if pool != "" {
|
||||
_, ipNet, err = net.ParseCIDR(pool)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
} else {
|
||||
ipNet = defaultPool
|
||||
}
|
||||
|
||||
return ipNet.String(), ipNet, nil, nil
|
||||
}
|
||||
|
||||
// ReleasePool releases the address pool - always succeeds
|
||||
func (a *allocator) ReleasePool(poolID string) error {
|
||||
log.Debugf("ReleasePool(%s)", poolID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestAddress returns an address from the specified pool ID.
|
||||
// Always allocate the 0.0.0.0/32 ip if no preferred address was specified
|
||||
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) %s", poolID, prefAddress, opts, opts["RequestAddressType"])
|
||||
_, ipNet, err := net.ParseCIDR(poolID)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if prefAddress == nil {
|
||||
return ipNet, nil, nil
|
||||
}
|
||||
return &net.IPNet{IP: prefAddress, Mask: ipNet.Mask}, nil, nil
|
||||
}
|
||||
|
||||
// ReleaseAddress releases the address - always succeeds
|
||||
func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
|
||||
log.Debugf("ReleaseAddress(%s, %v)", poolID, address)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverNew informs the allocator about a new global scope datastore
|
||||
func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DiscoverDelete is a notification of no interest for the allocator
|
||||
func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
|
||||
return nil
|
||||
}
|
|
@ -56,6 +56,9 @@ var (
|
|||
// GlobalKVProviderConfig constant represents the KV provider Config
|
||||
GlobalKVProviderConfig = MakeKVProviderConfig("global")
|
||||
|
||||
// GlobalKVClient constants represents the global kv store client
|
||||
GlobalKVClient = MakeKVClient("global")
|
||||
|
||||
// LocalKVProvider constant represents the KV provider backend
|
||||
LocalKVProvider = MakeKVProvider("local")
|
||||
|
||||
|
@ -64,6 +67,9 @@ var (
|
|||
|
||||
// LocalKVProviderConfig constant represents the KV provider Config
|
||||
LocalKVProviderConfig = MakeKVProviderConfig("local")
|
||||
|
||||
// LocalKVClient constants represents the local kv store client
|
||||
LocalKVClient = MakeKVClient("local")
|
||||
)
|
||||
|
||||
// MakeKVProvider returns the kvprovider label for the scope
|
||||
|
@ -81,6 +87,11 @@ func MakeKVProviderConfig(scope string) string {
|
|||
return DriverPrivatePrefix + scope + "kv_provider_config"
|
||||
}
|
||||
|
||||
// MakeKVClient returns the kv client label for the scope
|
||||
func MakeKVClient(scope string) string {
|
||||
return DriverPrivatePrefix + scope + "kv_client"
|
||||
}
|
||||
|
||||
// Key extracts the key portion of the label
|
||||
func Key(label string) (key string) {
|
||||
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
|
||||
|
|
|
@ -1004,7 +1004,7 @@ func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
|
|||
if ipVer == 6 {
|
||||
return nil
|
||||
}
|
||||
*cfgList = []*IpamConf{&IpamConf{}}
|
||||
*cfgList = []*IpamConf{{}}
|
||||
}
|
||||
|
||||
*infoList = make([]*IpamInfo, len(*cfgList))
|
||||
|
|
|
@ -85,7 +85,7 @@ func handleStopSignals(p proxy.Proxy) {
|
|||
s := make(chan os.Signal, 10)
|
||||
signal.Notify(s, os.Interrupt, syscall.SIGTERM, syscall.SIGSTOP)
|
||||
|
||||
for _ = range s {
|
||||
for range s {
|
||||
p.Close()
|
||||
|
||||
os.Exit(0)
|
||||
|
|
|
@ -35,7 +35,7 @@ const (
|
|||
dnsPort = "53"
|
||||
ptrIPv4domain = ".in-addr.arpa."
|
||||
ptrIPv6domain = ".ip6.arpa."
|
||||
respTTL = 1800
|
||||
respTTL = 600
|
||||
maxExtDNS = 3 //max number of external servers to try
|
||||
)
|
||||
|
||||
|
@ -147,6 +147,10 @@ func (r *resolver) ResolverOptions() []string {
|
|||
return []string{"ndots:0"}
|
||||
}
|
||||
|
||||
func setCommonFlags(msg *dns.Msg) {
|
||||
msg.RecursionAvailable = true
|
||||
}
|
||||
|
||||
func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error) {
|
||||
addr := r.sb.ResolveName(name)
|
||||
if addr == nil {
|
||||
|
@ -157,6 +161,7 @@ func (r *resolver) handleIPv4Query(name string, query *dns.Msg) (*dns.Msg, error
|
|||
|
||||
resp := new(dns.Msg)
|
||||
resp.SetReply(query)
|
||||
setCommonFlags(resp)
|
||||
|
||||
rr := new(dns.A)
|
||||
rr.Hdr = dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: respTTL}
|
||||
|
@ -186,6 +191,7 @@ func (r *resolver) handlePTRQuery(ptr string, query *dns.Msg) (*dns.Msg, error)
|
|||
|
||||
resp := new(dns.Msg)
|
||||
resp.SetReply(query)
|
||||
setCommonFlags(resp)
|
||||
|
||||
rr := new(dns.PTR)
|
||||
rr.Hdr = dns.RR_Header{Name: ptr, Rrtype: dns.TypePTR, Class: dns.ClassINET, Ttl: respTTL}
|
||||
|
@ -200,6 +206,9 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
|
|||
err error
|
||||
)
|
||||
|
||||
if query == nil || len(query.Question) == 0 {
|
||||
return
|
||||
}
|
||||
name := query.Question[0].Name
|
||||
if query.Question[0].Qtype == dns.TypeA {
|
||||
resp, err = r.handleIPv4Query(name, query)
|
||||
|
|
|
@ -4,19 +4,13 @@ import (
|
|||
"container/heap"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/etchosts"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/osl"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
|
@ -309,46 +303,6 @@ func (sb *sandbox) UnmarshalJSON(b []byte) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) startResolver() {
|
||||
sb.resolverOnce.Do(func() {
|
||||
var err error
|
||||
sb.resolver = NewResolver(sb)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
sb.resolver = nil
|
||||
}
|
||||
}()
|
||||
|
||||
err = sb.rebuildDNS()
|
||||
if err != nil {
|
||||
log.Errorf("Updating resolv.conf failed for container %s, %q", sb.ContainerID(), err)
|
||||
return
|
||||
}
|
||||
sb.resolver.SetExtServers(sb.extDNS)
|
||||
|
||||
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
|
||||
if err = sb.resolver.Start(); err != nil {
|
||||
log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (sb *sandbox) setupResolutionFiles() error {
|
||||
if err := sb.buildHostsFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sb.updateParentHosts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sb.setupDNS(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) Endpoints() []Endpoint {
|
||||
sb.Lock()
|
||||
defer sb.Unlock()
|
||||
|
@ -753,243 +707,6 @@ func (sb *sandbox) clearNetworkResources(origEp *endpoint) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
defaultPrefix = "/var/lib/docker/network/files"
|
||||
dirPerm = 0755
|
||||
filePerm = 0644
|
||||
)
|
||||
|
||||
func (sb *sandbox) buildHostsFile() error {
|
||||
if sb.config.hostsPath == "" {
|
||||
sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
|
||||
}
|
||||
|
||||
dir, _ := filepath.Split(sb.config.hostsPath)
|
||||
if err := createBasePath(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is for the host mode networking
|
||||
if sb.config.originHostsPath != "" {
|
||||
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 nil
|
||||
}
|
||||
|
||||
extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts))
|
||||
for _, extraHost := range sb.config.extraHosts {
|
||||
extraContent = append(extraContent, etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
|
||||
}
|
||||
|
||||
return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent)
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateHostsFile(ifaceIP string) error {
|
||||
var mhost string
|
||||
|
||||
if sb.config.originHostsPath != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sb.config.domainName != "" {
|
||||
mhost = fmt.Sprintf("%s.%s %s", sb.config.hostName, sb.config.domainName,
|
||||
sb.config.hostName)
|
||||
} else {
|
||||
mhost = sb.config.hostName
|
||||
}
|
||||
|
||||
extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
|
||||
|
||||
sb.addHostsEntries(extraContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) addHostsEntries(recs []etchosts.Record) {
|
||||
if err := etchosts.Add(sb.config.hostsPath, recs); err != nil {
|
||||
log.Warnf("Failed adding service host entries to the running container: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) {
|
||||
if err := etchosts.Delete(sb.config.hostsPath, recs); err != nil {
|
||||
log.Warnf("Failed deleting service host entries to the running container: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateParentHosts() error {
|
||||
var pSb Sandbox
|
||||
|
||||
for _, update := range sb.config.parentUpdates {
|
||||
sb.controller.WalkSandboxes(SandboxContainerWalker(&pSb, update.cid))
|
||||
if pSb == nil {
|
||||
continue
|
||||
}
|
||||
if err := etchosts.Update(pSb.(*sandbox).config.hostsPath, update.ip, update.name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) setupDNS() error {
|
||||
var newRC *resolvconf.File
|
||||
|
||||
if sb.config.resolvConfPath == "" {
|
||||
sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
|
||||
}
|
||||
|
||||
sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
|
||||
|
||||
dir, _ := filepath.Split(sb.config.resolvConfPath)
|
||||
if err := createBasePath(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is for the host mode networking
|
||||
if sb.config.originResolvConfPath != "" {
|
||||
if err := copyFile(sb.config.originResolvConfPath, sb.config.resolvConfPath); err != nil {
|
||||
return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", sb.config.originResolvConfPath, sb.config.resolvConfPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
currRC, err := resolvconf.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
|
||||
var (
|
||||
err error
|
||||
dnsList = resolvconf.GetNameservers(currRC.Content, netutils.IP)
|
||||
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
|
||||
dnsOptionsList = resolvconf.GetOptions(currRC.Content)
|
||||
)
|
||||
if len(sb.config.dnsList) > 0 {
|
||||
dnsList = sb.config.dnsList
|
||||
}
|
||||
if len(sb.config.dnsSearchList) > 0 {
|
||||
dnsSearchList = sb.config.dnsSearchList
|
||||
}
|
||||
if len(sb.config.dnsOptionsList) > 0 {
|
||||
dnsOptionsList = sb.config.dnsOptionsList
|
||||
}
|
||||
newRC, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true)
|
||||
if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// No contention on container resolv.conf file at sandbox creation
|
||||
if err := ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, filePerm); err != nil {
|
||||
return types.InternalErrorf("failed to write unhaltered resolv.conf file content when setting up dns for sandbox %s: %v", sb.ID(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Write hash
|
||||
if err := ioutil.WriteFile(sb.config.resolvConfHashFile, []byte(newRC.Hash), filePerm); err != nil {
|
||||
return types.InternalErrorf("failed to write resolv.conf hash file when setting up dns for sandbox %s: %v", sb.ID(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
|
||||
var (
|
||||
currHash string
|
||||
hashFile = sb.config.resolvConfHashFile
|
||||
)
|
||||
|
||||
// This is for the host mode networking
|
||||
if sb.config.originResolvConfPath != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
h, err := ioutil.ReadFile(hashFile)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
currHash = string(h)
|
||||
}
|
||||
}
|
||||
|
||||
if currHash != "" && currHash != currRC.Hash {
|
||||
// Seems the user has changed the container resolv.conf since the last time
|
||||
// we checked so return without doing anything.
|
||||
log.Infof("Skipping update of resolv.conf file with ipv6Enabled: %t because file was touched by user", ipv6Enabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled.
|
||||
newRC, err := resolvconf.FilterResolvDNS(currRC.Content, ipv6Enabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write the new hash in a temp file and rename it to make the update atomic
|
||||
dir := path.Dir(sb.config.resolvConfPath)
|
||||
tmpHashFile, err := ioutil.TempFile(dir, "hash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newRC.Hash), filePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmpHashFile.Name(), hashFile)
|
||||
}
|
||||
|
||||
// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
|
||||
// resolv.conf by doing the follwing
|
||||
// - Save the external name servers in resolv.conf in the sandbox
|
||||
// - Add only the embedded server's IP to container's resolv.conf
|
||||
// - If the embedded server needs any resolv.conf options add it to the current list
|
||||
func (sb *sandbox) rebuildDNS() error {
|
||||
currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// localhost entries have already been filtered out from the list
|
||||
// retain only the v4 servers in sb for forwarding the DNS queries
|
||||
sb.extDNS = resolvconf.GetNameservers(currRC.Content, netutils.IPv4)
|
||||
|
||||
var (
|
||||
dnsList = []string{sb.resolver.NameServer()}
|
||||
dnsOptionsList = resolvconf.GetOptions(currRC.Content)
|
||||
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
|
||||
)
|
||||
|
||||
// external v6 DNS servers has to be listed in resolv.conf
|
||||
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, netutils.IPv6)...)
|
||||
|
||||
// Resolver returns the options in the format resolv.conf expects
|
||||
dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)
|
||||
|
||||
_, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
|
||||
return err
|
||||
}
|
||||
|
||||
// joinLeaveStart waits to ensure there are no joins or leaves in progress and
|
||||
// marks this join/leave in progress without race
|
||||
func (sb *sandbox) joinLeaveStart() {
|
||||
|
@ -1191,32 +908,3 @@ func (eh *epHeap) Pop() interface{} {
|
|||
*eh = old[0 : n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
func createBasePath(dir string) error {
|
||||
return os.MkdirAll(dir, dirPerm)
|
||||
}
|
||||
|
||||
func createFile(path string) error {
|
||||
var f *os.File
|
||||
|
||||
dir, _ := filepath.Split(path)
|
||||
err := createBasePath(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err = os.Create(path)
|
||||
if err == nil {
|
||||
f.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
sBytes, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(dst, sBytes, filePerm)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
// +build !windows
|
||||
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libnetwork/etchosts"
|
||||
"github.com/docker/libnetwork/netutils"
|
||||
"github.com/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultPrefix = "/var/lib/docker/network/files"
|
||||
dirPerm = 0755
|
||||
filePerm = 0644
|
||||
)
|
||||
|
||||
func (sb *sandbox) startResolver() {
|
||||
sb.resolverOnce.Do(func() {
|
||||
var err error
|
||||
sb.resolver = NewResolver(sb)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
sb.resolver = nil
|
||||
}
|
||||
}()
|
||||
|
||||
err = sb.rebuildDNS()
|
||||
if err != nil {
|
||||
log.Errorf("Updating resolv.conf failed for container %s, %q", sb.ContainerID(), err)
|
||||
return
|
||||
}
|
||||
sb.resolver.SetExtServers(sb.extDNS)
|
||||
|
||||
sb.osSbox.InvokeFunc(sb.resolver.SetupFunc())
|
||||
if err = sb.resolver.Start(); err != nil {
|
||||
log.Errorf("Resolver Setup/Start failed for container %s, %q", sb.ContainerID(), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (sb *sandbox) setupResolutionFiles() error {
|
||||
if err := sb.buildHostsFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sb.updateParentHosts(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sb.setupDNS(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) buildHostsFile() error {
|
||||
if sb.config.hostsPath == "" {
|
||||
sb.config.hostsPath = defaultPrefix + "/" + sb.id + "/hosts"
|
||||
}
|
||||
|
||||
dir, _ := filepath.Split(sb.config.hostsPath)
|
||||
if err := createBasePath(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is for the host mode networking
|
||||
if sb.config.originHostsPath != "" {
|
||||
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 nil
|
||||
}
|
||||
|
||||
extraContent := make([]etchosts.Record, 0, len(sb.config.extraHosts))
|
||||
for _, extraHost := range sb.config.extraHosts {
|
||||
extraContent = append(extraContent, etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
|
||||
}
|
||||
|
||||
return etchosts.Build(sb.config.hostsPath, "", sb.config.hostName, sb.config.domainName, extraContent)
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateHostsFile(ifaceIP string) error {
|
||||
var mhost string
|
||||
|
||||
if sb.config.originHostsPath != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if sb.config.domainName != "" {
|
||||
mhost = fmt.Sprintf("%s.%s %s", sb.config.hostName, sb.config.domainName,
|
||||
sb.config.hostName)
|
||||
} else {
|
||||
mhost = sb.config.hostName
|
||||
}
|
||||
|
||||
extraContent := []etchosts.Record{{Hosts: mhost, IP: ifaceIP}}
|
||||
|
||||
sb.addHostsEntries(extraContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) addHostsEntries(recs []etchosts.Record) {
|
||||
if err := etchosts.Add(sb.config.hostsPath, recs); err != nil {
|
||||
log.Warnf("Failed adding service host entries to the running container: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) {
|
||||
if err := etchosts.Delete(sb.config.hostsPath, recs); err != nil {
|
||||
log.Warnf("Failed deleting service host entries to the running container: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateParentHosts() error {
|
||||
var pSb Sandbox
|
||||
|
||||
for _, update := range sb.config.parentUpdates {
|
||||
sb.controller.WalkSandboxes(SandboxContainerWalker(&pSb, update.cid))
|
||||
if pSb == nil {
|
||||
continue
|
||||
}
|
||||
if err := etchosts.Update(pSb.(*sandbox).config.hostsPath, update.ip, update.name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) setupDNS() error {
|
||||
var newRC *resolvconf.File
|
||||
|
||||
if sb.config.resolvConfPath == "" {
|
||||
sb.config.resolvConfPath = defaultPrefix + "/" + sb.id + "/resolv.conf"
|
||||
}
|
||||
|
||||
sb.config.resolvConfHashFile = sb.config.resolvConfPath + ".hash"
|
||||
|
||||
dir, _ := filepath.Split(sb.config.resolvConfPath)
|
||||
if err := createBasePath(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This is for the host mode networking
|
||||
if sb.config.originResolvConfPath != "" {
|
||||
if err := copyFile(sb.config.originResolvConfPath, sb.config.resolvConfPath); err != nil {
|
||||
return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", sb.config.originResolvConfPath, sb.config.resolvConfPath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
currRC, err := resolvconf.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
|
||||
var (
|
||||
err error
|
||||
dnsList = resolvconf.GetNameservers(currRC.Content, netutils.IP)
|
||||
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
|
||||
dnsOptionsList = resolvconf.GetOptions(currRC.Content)
|
||||
)
|
||||
if len(sb.config.dnsList) > 0 {
|
||||
dnsList = sb.config.dnsList
|
||||
}
|
||||
if len(sb.config.dnsSearchList) > 0 {
|
||||
dnsSearchList = sb.config.dnsSearchList
|
||||
}
|
||||
if len(sb.config.dnsOptionsList) > 0 {
|
||||
dnsOptionsList = sb.config.dnsOptionsList
|
||||
}
|
||||
newRC, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true)
|
||||
if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// No contention on container resolv.conf file at sandbox creation
|
||||
if err := ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, filePerm); err != nil {
|
||||
return types.InternalErrorf("failed to write unhaltered resolv.conf file content when setting up dns for sandbox %s: %v", sb.ID(), err)
|
||||
}
|
||||
}
|
||||
|
||||
// Write hash
|
||||
if err := ioutil.WriteFile(sb.config.resolvConfHashFile, []byte(newRC.Hash), filePerm); err != nil {
|
||||
return types.InternalErrorf("failed to write resolv.conf hash file when setting up dns for sandbox %s: %v", sb.ID(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
|
||||
var (
|
||||
currHash string
|
||||
hashFile = sb.config.resolvConfHashFile
|
||||
)
|
||||
|
||||
// This is for the host mode networking
|
||||
if sb.config.originResolvConfPath != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
h, err := ioutil.ReadFile(hashFile)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
currHash = string(h)
|
||||
}
|
||||
}
|
||||
|
||||
if currHash != "" && currHash != currRC.Hash {
|
||||
// Seems the user has changed the container resolv.conf since the last time
|
||||
// we checked so return without doing anything.
|
||||
log.Infof("Skipping update of resolv.conf file with ipv6Enabled: %t because file was touched by user", ipv6Enabled)
|
||||
return nil
|
||||
}
|
||||
|
||||
// replace any localhost/127.* and remove IPv6 nameservers if IPv6 disabled.
|
||||
newRC, err := resolvconf.FilterResolvDNS(currRC.Content, ipv6Enabled)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(sb.config.resolvConfPath, newRC.Content, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write the new hash in a temp file and rename it to make the update atomic
|
||||
dir := path.Dir(sb.config.resolvConfPath)
|
||||
tmpHashFile, err := ioutil.TempFile(dir, "hash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newRC.Hash), filePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmpHashFile.Name(), hashFile)
|
||||
}
|
||||
|
||||
// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
|
||||
// resolv.conf by doing the follwing
|
||||
// - Save the external name servers in resolv.conf in the sandbox
|
||||
// - Add only the embedded server's IP to container's resolv.conf
|
||||
// - If the embedded server needs any resolv.conf options add it to the current list
|
||||
func (sb *sandbox) rebuildDNS() error {
|
||||
currRC, err := resolvconf.GetSpecific(sb.config.resolvConfPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// localhost entries have already been filtered out from the list
|
||||
// retain only the v4 servers in sb for forwarding the DNS queries
|
||||
sb.extDNS = resolvconf.GetNameservers(currRC.Content, netutils.IPv4)
|
||||
|
||||
var (
|
||||
dnsList = []string{sb.resolver.NameServer()}
|
||||
dnsOptionsList = resolvconf.GetOptions(currRC.Content)
|
||||
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
|
||||
)
|
||||
|
||||
// external v6 DNS servers has to be listed in resolv.conf
|
||||
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, netutils.IPv6)...)
|
||||
|
||||
// Resolver returns the options in the format resolv.conf expects
|
||||
dnsOptionsList = append(dnsOptionsList, sb.resolver.ResolverOptions()...)
|
||||
|
||||
_, err = resolvconf.Build(sb.config.resolvConfPath, dnsList, dnsSearchList, dnsOptionsList)
|
||||
return err
|
||||
}
|
||||
|
||||
func createBasePath(dir string) error {
|
||||
return os.MkdirAll(dir, dirPerm)
|
||||
}
|
||||
|
||||
func createFile(path string) error {
|
||||
var f *os.File
|
||||
|
||||
dir, _ := filepath.Split(path)
|
||||
err := createBasePath(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err = os.Create(path)
|
||||
if err == nil {
|
||||
f.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
sBytes, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(dst, sBytes, filePerm)
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// +build windows
|
||||
|
||||
package libnetwork
|
||||
|
||||
import (
|
||||
"github.com/docker/libnetwork/etchosts"
|
||||
)
|
||||
|
||||
// Stub implementations for DNS related functions
|
||||
|
||||
func (sb *sandbox) startResolver() {
|
||||
}
|
||||
|
||||
func (sb *sandbox) setupResolutionFiles() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateHostsFile(ifaceIP string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sb *sandbox) addHostsEntries(recs []etchosts.Record) {
|
||||
|
||||
}
|
||||
|
||||
func (sb *sandbox) deleteHostsEntries(recs []etchosts.Record) {
|
||||
|
||||
}
|
||||
|
||||
func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
|
||||
return nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// +build !windows
|
||||
// +build linux freebsd
|
||||
|
||||
package libnetwork
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ func (c *controller) initStores() error {
|
|||
return nil
|
||||
}
|
||||
scopeConfigs := c.cfg.Scopes
|
||||
c.stores = nil
|
||||
c.Unlock()
|
||||
|
||||
for scope, scfg := range scopeConfigs {
|
||||
|
@ -418,6 +419,9 @@ func (c *controller) watchLoop() {
|
|||
}
|
||||
|
||||
func (c *controller) startWatch() {
|
||||
if c.watchCh != nil {
|
||||
return
|
||||
}
|
||||
c.watchCh = make(chan *endpoint)
|
||||
c.unWatchCh = make(chan *endpoint)
|
||||
c.nmap = make(map[string]*netWatch)
|
||||
|
|
Загрузка…
Ссылка в новой задаче