2016-03-30 21:42:58 +00:00
|
|
|
package libnetwork
|
|
|
|
|
2016-05-17 21:12:39 +00:00
|
|
|
//go:generate protoc -I.:Godeps/_workspace/src/github.com/gogo/protobuf --gogo_out=import_path=github.com/docker/libnetwork,Mgogoproto/gogo.proto=github.com/gogo/protobuf/gogoproto:. agent.proto
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
import (
|
2017-01-31 17:13:08 +00:00
|
|
|
"encoding/json"
|
2016-03-30 21:42:58 +00:00
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
2016-06-04 09:10:19 +00:00
|
|
|
"sort"
|
2016-11-22 07:38:03 +00:00
|
|
|
"sync"
|
2016-03-30 21:42:58 +00:00
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
2016-09-16 22:15:46 +00:00
|
|
|
"github.com/docker/docker/pkg/stringid"
|
2016-03-30 21:42:58 +00:00
|
|
|
"github.com/docker/go-events"
|
|
|
|
"github.com/docker/libnetwork/datastore"
|
|
|
|
"github.com/docker/libnetwork/discoverapi"
|
|
|
|
"github.com/docker/libnetwork/driverapi"
|
|
|
|
"github.com/docker/libnetwork/networkdb"
|
2016-06-04 09:10:19 +00:00
|
|
|
"github.com/docker/libnetwork/types"
|
2016-05-17 21:12:39 +00:00
|
|
|
"github.com/gogo/protobuf/proto"
|
2016-03-30 21:42:58 +00:00
|
|
|
)
|
|
|
|
|
2016-06-11 11:50:25 +00:00
|
|
|
const (
|
|
|
|
subsysGossip = "networking:gossip"
|
|
|
|
subsysIPSec = "networking:ipsec"
|
|
|
|
keyringSize = 3
|
|
|
|
)
|
|
|
|
|
2016-06-04 09:10:19 +00:00
|
|
|
// ByTime implements sort.Interface for []*types.EncryptionKey based on
|
|
|
|
// the LamportTime field.
|
|
|
|
type ByTime []*types.EncryptionKey
|
|
|
|
|
|
|
|
func (b ByTime) Len() int { return len(b) }
|
|
|
|
func (b ByTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
|
|
|
func (b ByTime) Less(i, j int) bool { return b[i].LamportTime < b[j].LamportTime }
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
type agent struct {
|
|
|
|
networkDB *networkdb.NetworkDB
|
|
|
|
bindAddr string
|
2016-07-20 01:17:30 +00:00
|
|
|
advertiseAddr string
|
2017-04-13 01:51:01 +00:00
|
|
|
dataPathAddr string
|
2016-03-30 21:42:58 +00:00
|
|
|
epTblCancel func()
|
|
|
|
driverCancelFuncs map[string][]func()
|
2016-11-22 07:38:03 +00:00
|
|
|
sync.Mutex
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-13 01:51:01 +00:00
|
|
|
func (a *agent) dataPathAddress() string {
|
|
|
|
a.Lock()
|
|
|
|
defer a.Unlock()
|
|
|
|
if a.dataPathAddr != "" {
|
|
|
|
return a.dataPathAddr
|
|
|
|
}
|
|
|
|
return a.advertiseAddr
|
|
|
|
}
|
|
|
|
|
2017-03-02 07:57:37 +00:00
|
|
|
const libnetworkEPTable = "endpoint_table"
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
func getBindAddr(ifaceName string) (string, error) {
|
|
|
|
iface, err := net.InterfaceByName(ifaceName)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to find interface %s: %v", ifaceName, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
addrs, err := iface.Addrs()
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to get interface addresses: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, a := range addrs {
|
|
|
|
addr, ok := a.(*net.IPNet)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
addrIP := addr.IP
|
|
|
|
|
|
|
|
if addrIP.IsLinkLocalUnicast() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return addrIP.String(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", fmt.Errorf("failed to get bind address")
|
|
|
|
}
|
|
|
|
|
|
|
|
func resolveAddr(addrOrInterface string) (string, error) {
|
|
|
|
// Try and see if this is a valid IP address
|
|
|
|
if net.ParseIP(addrOrInterface) != nil {
|
|
|
|
return addrOrInterface, nil
|
|
|
|
}
|
|
|
|
|
2016-06-12 17:08:26 +00:00
|
|
|
addr, err := net.ResolveIPAddr("ip", addrOrInterface)
|
|
|
|
if err != nil {
|
|
|
|
// If not a valid IP address, it should be a valid interface
|
|
|
|
return getBindAddr(addrOrInterface)
|
|
|
|
}
|
|
|
|
return addr.String(), nil
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
2016-06-05 05:48:10 +00:00
|
|
|
func (c *controller) handleKeyChange(keys []*types.EncryptionKey) error {
|
2016-06-07 01:17:10 +00:00
|
|
|
drvEnc := discoverapi.DriverEncryptionUpdate{}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
a := c.getAgent()
|
|
|
|
if a == nil {
|
|
|
|
logrus.Debug("Skipping key change as agent is nil")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-11 11:50:25 +00:00
|
|
|
// Find the deleted key. If the deleted key was the primary key,
|
|
|
|
// a new primary key should be set before removing if from keyring.
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Lock()
|
|
|
|
added := []byte{}
|
2016-06-11 11:50:25 +00:00
|
|
|
deleted := []byte{}
|
|
|
|
j := len(c.keys)
|
|
|
|
for i := 0; i < j; {
|
|
|
|
same := false
|
|
|
|
for _, key := range keys {
|
|
|
|
if same = key.LamportTime == c.keys[i].LamportTime; same {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !same {
|
|
|
|
cKey := c.keys[i]
|
|
|
|
if cKey.Subsystem == subsysGossip {
|
|
|
|
deleted = cKey.Key
|
|
|
|
}
|
|
|
|
|
2016-06-15 11:10:23 +00:00
|
|
|
if cKey.Subsystem == subsysIPSec {
|
2016-06-11 11:50:25 +00:00
|
|
|
drvEnc.Prune = cKey.Key
|
|
|
|
drvEnc.PruneTag = cKey.LamportTime
|
|
|
|
}
|
|
|
|
c.keys[i], c.keys[j-1] = c.keys[j-1], c.keys[i]
|
|
|
|
c.keys[j-1] = nil
|
|
|
|
j--
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
c.keys = c.keys[:j]
|
|
|
|
|
|
|
|
// Find the new key and add it to the key ring
|
|
|
|
for _, key := range keys {
|
|
|
|
same := false
|
|
|
|
for _, cKey := range c.keys {
|
|
|
|
if same = cKey.LamportTime == key.LamportTime; same {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !same {
|
|
|
|
c.keys = append(c.keys, key)
|
|
|
|
if key.Subsystem == subsysGossip {
|
2016-11-22 07:38:03 +00:00
|
|
|
added = key.Key
|
2016-06-11 11:50:25 +00:00
|
|
|
}
|
|
|
|
|
2016-06-15 11:10:23 +00:00
|
|
|
if key.Subsystem == subsysIPSec {
|
2016-06-11 11:50:25 +00:00
|
|
|
drvEnc.Key = key.Key
|
|
|
|
drvEnc.Tag = key.LamportTime
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Unlock()
|
|
|
|
|
|
|
|
if len(added) > 0 {
|
|
|
|
a.networkDB.SetKey(added)
|
|
|
|
}
|
2016-06-11 11:50:25 +00:00
|
|
|
|
2016-08-03 01:34:10 +00:00
|
|
|
key, tag, err := c.getPrimaryKeyTag(subsysGossip)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-06-11 11:50:25 +00:00
|
|
|
a.networkDB.SetPrimaryKey(key)
|
|
|
|
|
2016-08-03 01:34:10 +00:00
|
|
|
key, tag, err = c.getPrimaryKeyTag(subsysIPSec)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-06-11 11:50:25 +00:00
|
|
|
drvEnc.Primary = key
|
|
|
|
drvEnc.PrimaryTag = tag
|
|
|
|
|
|
|
|
if len(deleted) > 0 {
|
|
|
|
a.networkDB.RemoveKey(deleted)
|
|
|
|
}
|
|
|
|
|
|
|
|
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
|
|
|
err := driver.DiscoverNew(discoverapi.EncryptionKeysUpdate, drvEnc)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Warnf("Failed to update datapath keys in driver %s: %v", name, err)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-05 05:48:10 +00:00
|
|
|
func (c *controller) agentSetup() error {
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Lock()
|
2016-06-05 05:48:10 +00:00
|
|
|
clusterProvider := c.cfg.Daemon.ClusterProvider
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := c.agent
|
|
|
|
c.Unlock()
|
2017-04-18 18:29:25 +00:00
|
|
|
|
|
|
|
if clusterProvider == nil {
|
|
|
|
msg := "Aborting initialization of Libnetwork Agent because cluster provider is now unset"
|
|
|
|
logrus.Errorf(msg)
|
|
|
|
return fmt.Errorf(msg)
|
|
|
|
}
|
|
|
|
|
2016-07-20 01:17:30 +00:00
|
|
|
bindAddr := clusterProvider.GetLocalAddress()
|
|
|
|
advAddr := clusterProvider.GetAdvertiseAddress()
|
2017-04-13 01:51:01 +00:00
|
|
|
dataAddr := clusterProvider.GetDataPathAddress()
|
2017-04-27 23:58:42 +00:00
|
|
|
remoteList := clusterProvider.GetRemoteAddressList()
|
|
|
|
remoteAddrList := make([]string, len(remoteList))
|
|
|
|
for _, remote := range remoteList {
|
|
|
|
addr, _, _ := net.SplitHostPort(remote)
|
|
|
|
remoteAddrList = append(remoteAddrList, addr)
|
|
|
|
}
|
|
|
|
|
2016-09-22 18:38:35 +00:00
|
|
|
listen := clusterProvider.GetListenAddress()
|
|
|
|
listenAddr, _, _ := net.SplitHostPort(listen)
|
2016-06-05 05:48:10 +00:00
|
|
|
|
2017-04-27 23:58:42 +00:00
|
|
|
logrus.Infof("Initializing Libnetwork Agent Listen-Addr=%s Local-addr=%s Adv-addr=%s Data-addr=%s Remote-addr-list=%v",
|
|
|
|
listenAddr, bindAddr, advAddr, dataAddr, remoteAddrList)
|
2016-11-22 07:38:03 +00:00
|
|
|
if advAddr != "" && agent == nil {
|
2017-04-13 01:51:01 +00:00
|
|
|
if err := c.agentInit(listenAddr, bindAddr, advAddr, dataAddr); err != nil {
|
2016-06-05 05:48:10 +00:00
|
|
|
logrus.Errorf("Error in agentInit : %v", err)
|
|
|
|
} else {
|
|
|
|
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
|
|
|
if capability.DataScope == datastore.GlobalScope {
|
|
|
|
c.agentDriverNotify(driver)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2016-08-20 00:57:58 +00:00
|
|
|
|
2017-04-27 23:58:42 +00:00
|
|
|
if len(remoteAddrList) > 0 {
|
|
|
|
if err := c.agentJoin(remoteAddrList); err != nil {
|
2016-09-27 07:12:07 +00:00
|
|
|
logrus.Errorf("Error in joining gossip cluster : %v(join will be retried in background)", err)
|
2016-06-05 05:48:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-20 00:57:58 +00:00
|
|
|
|
2016-08-24 16:51:02 +00:00
|
|
|
c.Lock()
|
|
|
|
if c.agent != nil && c.agentInitDone != nil {
|
2016-08-20 00:57:58 +00:00
|
|
|
close(c.agentInitDone)
|
2016-08-24 16:51:02 +00:00
|
|
|
c.agentInitDone = nil
|
2017-04-05 17:38:24 +00:00
|
|
|
c.agentStopDone = make(chan struct{})
|
2016-08-20 00:57:58 +00:00
|
|
|
}
|
2016-08-24 16:51:02 +00:00
|
|
|
c.Unlock()
|
2016-08-20 00:57:58 +00:00
|
|
|
|
2016-06-05 05:48:10 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-11 11:50:25 +00:00
|
|
|
// For a given subsystem getKeys sorts the keys by lamport time and returns
|
|
|
|
// slice of keys and lamport time which can used as a unique tag for the keys
|
|
|
|
func (c *controller) getKeys(subsys string) ([][]byte, []uint64) {
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
|
2016-06-11 11:50:25 +00:00
|
|
|
sort.Sort(ByTime(c.keys))
|
|
|
|
|
|
|
|
keys := [][]byte{}
|
|
|
|
tags := []uint64{}
|
|
|
|
for _, key := range c.keys {
|
|
|
|
if key.Subsystem == subsys {
|
|
|
|
keys = append(keys, key.Key)
|
|
|
|
tags = append(tags, key.LamportTime)
|
|
|
|
}
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 11:50:25 +00:00
|
|
|
keys[0], keys[1] = keys[1], keys[0]
|
|
|
|
tags[0], tags[1] = tags[1], tags[0]
|
|
|
|
return keys, tags
|
|
|
|
}
|
2016-06-07 01:17:10 +00:00
|
|
|
|
2016-08-03 01:34:10 +00:00
|
|
|
// getPrimaryKeyTag returns the primary key for a given subsystem from the
|
2016-06-11 11:50:25 +00:00
|
|
|
// list of sorted key and the associated tag
|
2016-08-03 01:34:10 +00:00
|
|
|
func (c *controller) getPrimaryKeyTag(subsys string) ([]byte, uint64, error) {
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
2016-06-05 05:48:10 +00:00
|
|
|
sort.Sort(ByTime(c.keys))
|
2016-06-11 11:50:25 +00:00
|
|
|
keys := []*types.EncryptionKey{}
|
2016-06-05 05:48:10 +00:00
|
|
|
for _, key := range c.keys {
|
2016-06-11 11:50:25 +00:00
|
|
|
if key.Subsystem == subsys {
|
|
|
|
keys = append(keys, key)
|
2016-06-07 01:17:10 +00:00
|
|
|
}
|
2016-06-04 09:10:19 +00:00
|
|
|
}
|
2016-08-03 01:34:10 +00:00
|
|
|
return keys[1].Key, keys[1].LamportTime, nil
|
2016-06-11 11:50:25 +00:00
|
|
|
}
|
|
|
|
|
2017-04-13 01:51:01 +00:00
|
|
|
func (c *controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr, dataPathAddr string) error {
|
2016-06-11 11:50:25 +00:00
|
|
|
if !c.isAgent() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
bindAddr, err := resolveAddr(bindAddrOrInterface)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-06-15 11:10:23 +00:00
|
|
|
keys, tags := c.getKeys(subsysGossip)
|
2016-03-30 21:42:58 +00:00
|
|
|
hostname, _ := os.Hostname()
|
2016-09-16 22:15:46 +00:00
|
|
|
nodeName := hostname + "-" + stringid.TruncateID(stringid.GenerateRandomID())
|
|
|
|
logrus.Info("Gossip cluster hostname ", nodeName)
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
nDB, err := networkdb.New(&networkdb.Config{
|
2016-09-22 18:38:35 +00:00
|
|
|
BindAddr: listenAddr,
|
2016-07-20 01:17:30 +00:00
|
|
|
AdvertiseAddr: advertiseAddr,
|
2016-09-16 22:15:46 +00:00
|
|
|
NodeName: nodeName,
|
2016-07-20 01:17:30 +00:00
|
|
|
Keys: keys,
|
2016-03-30 21:42:58 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-03-02 07:57:37 +00:00
|
|
|
ch, cancel := nDB.Watch(libnetworkEPTable, "", "")
|
2017-01-31 17:13:08 +00:00
|
|
|
nodeCh, cancel := nDB.Watch(networkdb.NodeTable, "", "")
|
2016-03-30 21:42:58 +00:00
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Lock()
|
2016-03-30 21:42:58 +00:00
|
|
|
c.agent = &agent{
|
|
|
|
networkDB: nDB,
|
|
|
|
bindAddr: bindAddr,
|
2016-07-20 01:17:30 +00:00
|
|
|
advertiseAddr: advertiseAddr,
|
2017-04-13 01:51:01 +00:00
|
|
|
dataPathAddr: dataPathAddr,
|
2016-03-30 21:42:58 +00:00
|
|
|
epTblCancel: cancel,
|
|
|
|
driverCancelFuncs: make(map[string][]func()),
|
|
|
|
}
|
2016-11-22 07:38:03 +00:00
|
|
|
c.Unlock()
|
2016-03-30 21:42:58 +00:00
|
|
|
|
|
|
|
go c.handleTableEvents(ch, c.handleEpTableEvent)
|
2017-01-31 17:13:08 +00:00
|
|
|
go c.handleTableEvents(nodeCh, c.handleNodeTableEvent)
|
2016-06-07 01:17:10 +00:00
|
|
|
|
2016-06-15 11:10:23 +00:00
|
|
|
drvEnc := discoverapi.DriverEncryptionConfig{}
|
|
|
|
keys, tags = c.getKeys(subsysIPSec)
|
|
|
|
drvEnc.Keys = keys
|
|
|
|
drvEnc.Tags = tags
|
|
|
|
|
2016-06-07 01:17:10 +00:00
|
|
|
c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
|
|
|
|
err := driver.DiscoverNew(discoverapi.EncryptionKeysConfig, drvEnc)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Warnf("Failed to set datapath keys in driver %s: %v", name, err)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
|
2016-07-13 00:35:32 +00:00
|
|
|
c.WalkNetworks(joinCluster)
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-04-27 23:58:42 +00:00
|
|
|
func (c *controller) agentJoin(remoteAddrList []string) error {
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := c.getAgent()
|
|
|
|
if agent == nil {
|
2016-03-30 21:42:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
2017-04-27 23:58:42 +00:00
|
|
|
return agent.networkDB.Join(remoteAddrList)
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *controller) agentDriverNotify(d driverapi.Driver) {
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := c.getAgent()
|
|
|
|
if agent == nil {
|
2016-03-30 21:42:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-04-13 01:51:01 +00:00
|
|
|
if err := d.DiscoverNew(discoverapi.NodeDiscovery, discoverapi.NodeDiscoveryData{
|
|
|
|
Address: agent.dataPathAddress(),
|
2016-11-22 07:38:03 +00:00
|
|
|
BindAddress: agent.bindAddr,
|
2016-07-20 01:17:30 +00:00
|
|
|
Self: true,
|
2017-04-13 01:51:01 +00:00
|
|
|
}); err != nil {
|
|
|
|
logrus.Warnf("Failed the node discovery in driver: %v", err)
|
|
|
|
}
|
2016-06-07 01:17:10 +00:00
|
|
|
|
|
|
|
drvEnc := discoverapi.DriverEncryptionConfig{}
|
2016-06-15 11:10:23 +00:00
|
|
|
keys, tags := c.getKeys(subsysIPSec)
|
2016-06-11 11:50:25 +00:00
|
|
|
drvEnc.Keys = keys
|
|
|
|
drvEnc.Tags = tags
|
|
|
|
|
2017-04-13 01:51:01 +00:00
|
|
|
if err := d.DiscoverNew(discoverapi.EncryptionKeysConfig, drvEnc); err != nil {
|
|
|
|
logrus.Warnf("Failed to set datapath keys in driver: %v", err)
|
|
|
|
}
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *controller) agentClose() {
|
2016-11-01 21:32:19 +00:00
|
|
|
// Acquire current agent instance and reset its pointer
|
|
|
|
// then run closing functions
|
|
|
|
c.Lock()
|
|
|
|
agent := c.agent
|
|
|
|
c.agent = nil
|
|
|
|
c.Unlock()
|
|
|
|
|
|
|
|
if agent == nil {
|
2016-03-30 21:42:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
var cancelList []func()
|
|
|
|
|
|
|
|
agent.Lock()
|
2016-11-01 21:32:19 +00:00
|
|
|
for _, cancelFuncs := range agent.driverCancelFuncs {
|
2016-03-30 21:42:58 +00:00
|
|
|
for _, cancel := range cancelFuncs {
|
2016-11-22 07:38:03 +00:00
|
|
|
cancelList = append(cancelList, cancel)
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
}
|
2016-11-22 07:38:03 +00:00
|
|
|
agent.Unlock()
|
|
|
|
|
|
|
|
for _, cancel := range cancelList {
|
|
|
|
cancel()
|
|
|
|
}
|
2016-03-30 21:42:58 +00:00
|
|
|
|
2016-11-01 21:32:19 +00:00
|
|
|
agent.epTblCancel()
|
2016-08-17 00:37:33 +00:00
|
|
|
|
2016-11-01 21:32:19 +00:00
|
|
|
agent.networkDB.Close()
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
2017-03-02 07:57:37 +00:00
|
|
|
// Task has the backend container details
|
|
|
|
type Task struct {
|
|
|
|
Name string
|
|
|
|
EndpointID string
|
|
|
|
EndpointIP string
|
|
|
|
Info map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceInfo has service specific details along with the list of backend tasks
|
|
|
|
type ServiceInfo struct {
|
|
|
|
VIP string
|
|
|
|
LocalLBIndex int
|
|
|
|
Tasks []Task
|
|
|
|
Ports []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type epRecord struct {
|
|
|
|
ep EndpointRecord
|
|
|
|
info map[string]string
|
|
|
|
lbIndex int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *network) Services() map[string]ServiceInfo {
|
|
|
|
eps := make(map[string]epRecord)
|
|
|
|
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
agent := n.getController().getAgent()
|
|
|
|
if agent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk through libnetworkEPTable and fetch the driver agnostic endpoint info
|
|
|
|
entries := agent.networkDB.GetTableByNetwork(libnetworkEPTable, n.id)
|
|
|
|
for eid, value := range entries {
|
|
|
|
var epRec EndpointRecord
|
|
|
|
nid := n.ID()
|
|
|
|
if err := proto.Unmarshal(value.([]byte), &epRec); err != nil {
|
|
|
|
logrus.Errorf("Unmarshal of libnetworkEPTable failed for endpoint %s in network %s, %v", eid, nid, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
i := n.getController().getLBIndex(epRec.ServiceID, nid, epRec.IngressPorts)
|
|
|
|
eps[eid] = epRecord{
|
|
|
|
ep: epRec,
|
|
|
|
lbIndex: i,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk through the driver's tables, have the driver decode the entries
|
|
|
|
// and return the tuple {ep ID, value}. value is a string that coveys
|
|
|
|
// relevant info about the endpoint.
|
|
|
|
d, err := n.driver(true)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Could not resolve driver for network %s/%s while fetching services: %v", n.networkType, n.ID(), err)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
for _, table := range n.driverTables {
|
|
|
|
if table.objType != driverapi.EndpointObject {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
entries := agent.networkDB.GetTableByNetwork(table.name, n.id)
|
|
|
|
for key, value := range entries {
|
|
|
|
epID, info := d.DecodeTableEntry(table.name, key, value.([]byte))
|
|
|
|
if ep, ok := eps[epID]; !ok {
|
|
|
|
logrus.Errorf("Inconsistent driver and libnetwork state for endpoint %s", epID)
|
|
|
|
} else {
|
|
|
|
ep.info = info
|
|
|
|
eps[epID] = ep
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// group the endpoints into a map keyed by the service name
|
|
|
|
sinfo := make(map[string]ServiceInfo)
|
|
|
|
for ep, epr := range eps {
|
|
|
|
var (
|
|
|
|
s ServiceInfo
|
|
|
|
ok bool
|
|
|
|
)
|
|
|
|
if s, ok = sinfo[epr.ep.ServiceName]; !ok {
|
|
|
|
s = ServiceInfo{
|
|
|
|
VIP: epr.ep.VirtualIP,
|
|
|
|
LocalLBIndex: epr.lbIndex,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ports := []string{}
|
|
|
|
if s.Ports == nil {
|
|
|
|
for _, port := range epr.ep.IngressPorts {
|
|
|
|
p := fmt.Sprintf("Target: %d, Publish: %d", port.TargetPort, port.PublishedPort)
|
|
|
|
ports = append(ports, p)
|
|
|
|
}
|
|
|
|
s.Ports = ports
|
|
|
|
}
|
|
|
|
s.Tasks = append(s.Tasks, Task{
|
|
|
|
Name: epr.ep.Name,
|
|
|
|
EndpointID: ep,
|
|
|
|
EndpointIP: epr.ep.EndpointIP,
|
|
|
|
Info: epr.info,
|
|
|
|
})
|
|
|
|
sinfo[epr.ep.ServiceName] = s
|
|
|
|
}
|
|
|
|
return sinfo
|
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
func (n *network) isClusterEligible() bool {
|
|
|
|
if n.driverScope() != datastore.GlobalScope {
|
|
|
|
return false
|
|
|
|
}
|
2016-11-22 07:38:03 +00:00
|
|
|
return n.getController().getAgent() != nil
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *network) joinCluster() error {
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := n.getController().getAgent()
|
|
|
|
if agent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return agent.networkDB.JoinNetwork(n.ID())
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (n *network) leaveCluster() error {
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := n.getController().getAgent()
|
|
|
|
if agent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return agent.networkDB.LeaveNetwork(n.ID())
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
|
2016-11-11 08:42:34 +00:00
|
|
|
func (ep *endpoint) addDriverInfoToCluster() error {
|
|
|
|
n := ep.getNetwork()
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if ep.joinInfo == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := n.getController().getAgent()
|
2016-11-11 08:42:34 +00:00
|
|
|
if agent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, te := range ep.joinInfo.driverTableEntries {
|
|
|
|
if err := agent.networkDB.CreateEntry(te.tableName, n.ID(), te.key, te.value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ep *endpoint) deleteDriverInfoFromCluster() error {
|
|
|
|
n := ep.getNetwork()
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if ep.joinInfo == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := n.getController().getAgent()
|
2016-11-11 08:42:34 +00:00
|
|
|
if agent == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, te := range ep.joinInfo.driverTableEntries {
|
|
|
|
if err := agent.networkDB.DeleteEntry(te.tableName, n.ID(), te.key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ep *endpoint) addServiceInfoToCluster() error {
|
2017-02-15 23:00:19 +00:00
|
|
|
if ep.isAnonymous() && len(ep.myAliases) == 0 || ep.Iface().Address() == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
n := ep.getNetwork()
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
c := n.getController()
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := c.getAgent()
|
2016-05-31 06:55:51 +00:00
|
|
|
|
2017-02-15 23:00:19 +00:00
|
|
|
var ingressPorts []*PortConfig
|
|
|
|
if ep.svcID != "" {
|
|
|
|
// Gossip ingress ports only in ingress network.
|
|
|
|
if n.ingress {
|
|
|
|
ingressPorts = ep.ingressPorts
|
2016-04-14 00:53:41 +00:00
|
|
|
}
|
|
|
|
|
2017-02-15 23:00:19 +00:00
|
|
|
if err := c.addServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), ep.virtualIP, ingressPorts, ep.svcAliases, ep.Iface().Address().IP); err != nil {
|
2016-05-17 21:12:39 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-15 23:00:19 +00:00
|
|
|
}
|
2016-05-17 21:12:39 +00:00
|
|
|
|
2017-02-15 23:00:19 +00:00
|
|
|
name := ep.Name()
|
|
|
|
if ep.isAnonymous() {
|
|
|
|
name = ep.MyAliases()[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := proto.Marshal(&EndpointRecord{
|
|
|
|
Name: name,
|
|
|
|
ServiceName: ep.svcName,
|
|
|
|
ServiceID: ep.svcID,
|
|
|
|
VirtualIP: ep.virtualIP.String(),
|
|
|
|
IngressPorts: ingressPorts,
|
|
|
|
Aliases: ep.svcAliases,
|
|
|
|
TaskAliases: ep.myAliases,
|
|
|
|
EndpointIP: ep.Iface().Address().IP.String(),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if agent != nil {
|
2017-03-02 07:57:37 +00:00
|
|
|
if err := agent.networkDB.CreateEntry(libnetworkEPTable, n.ID(), ep.ID(), buf); err != nil {
|
2017-02-15 23:00:19 +00:00
|
|
|
return err
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-11-11 08:42:34 +00:00
|
|
|
func (ep *endpoint) deleteServiceInfoFromCluster() error {
|
2017-02-15 23:00:19 +00:00
|
|
|
if ep.isAnonymous() && len(ep.myAliases) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
n := ep.getNetwork()
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
c := n.getController()
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := c.getAgent()
|
|
|
|
|
2017-02-15 23:00:19 +00:00
|
|
|
if ep.svcID != "" && ep.Iface().Address() != nil {
|
|
|
|
var ingressPorts []*PortConfig
|
|
|
|
if n.ingress {
|
|
|
|
ingressPorts = ep.ingressPorts
|
|
|
|
}
|
2016-05-31 06:55:51 +00:00
|
|
|
|
2017-02-15 23:00:19 +00:00
|
|
|
if err := c.rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), ep.virtualIP, ingressPorts, ep.svcAliases, ep.Iface().Address().IP); err != nil {
|
|
|
|
return err
|
2016-04-14 00:53:41 +00:00
|
|
|
}
|
2017-02-15 23:00:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if agent != nil {
|
2017-03-02 07:57:37 +00:00
|
|
|
if err := agent.networkDB.DeleteEntry(libnetworkEPTable, n.ID(), ep.ID()); err != nil {
|
2017-02-15 23:00:19 +00:00
|
|
|
return err
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-15 23:00:19 +00:00
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *network) addDriverWatches() {
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c := n.getController()
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := c.getAgent()
|
|
|
|
if agent == nil {
|
|
|
|
return
|
|
|
|
}
|
2017-03-02 07:57:37 +00:00
|
|
|
for _, table := range n.driverTables {
|
|
|
|
ch, cancel := agent.networkDB.Watch(table.name, n.ID(), "")
|
2016-11-22 07:38:03 +00:00
|
|
|
agent.Lock()
|
|
|
|
agent.driverCancelFuncs[n.ID()] = append(agent.driverCancelFuncs[n.ID()], cancel)
|
|
|
|
agent.Unlock()
|
2016-03-30 21:42:58 +00:00
|
|
|
go c.handleTableEvents(ch, n.handleDriverTableEvent)
|
|
|
|
d, err := n.driver(false)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Could not resolve driver %s while walking driver tabl: %v", n.networkType, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-03-02 07:57:37 +00:00
|
|
|
agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte) bool {
|
2016-08-08 18:55:06 +00:00
|
|
|
if nid == n.ID() {
|
2017-03-02 07:57:37 +00:00
|
|
|
d.EventNotify(driverapi.Create, nid, table.name, key, value)
|
2016-08-08 18:55:06 +00:00
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
return false
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *network) cancelDriverWatches() {
|
|
|
|
if !n.isClusterEligible() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-11-22 07:38:03 +00:00
|
|
|
agent := n.getController().getAgent()
|
|
|
|
if agent == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
agent.Lock()
|
|
|
|
cancelFuncs := agent.driverCancelFuncs[n.ID()]
|
|
|
|
delete(agent.driverCancelFuncs, n.ID())
|
|
|
|
agent.Unlock()
|
2016-03-30 21:42:58 +00:00
|
|
|
|
|
|
|
for _, cancel := range cancelFuncs {
|
|
|
|
cancel()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *controller) handleTableEvents(ch chan events.Event, fn func(events.Event)) {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case ev, ok := <-ch:
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fn(ev)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *network) handleDriverTableEvent(ev events.Event) {
|
|
|
|
d, err := n.driver(false)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Could not resolve driver %s while handling driver table event: %v", n.networkType, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
etype driverapi.EventType
|
|
|
|
tname string
|
|
|
|
key string
|
|
|
|
value []byte
|
|
|
|
)
|
|
|
|
|
|
|
|
switch event := ev.(type) {
|
|
|
|
case networkdb.CreateEvent:
|
|
|
|
tname = event.Table
|
|
|
|
key = event.Key
|
|
|
|
value = event.Value
|
|
|
|
etype = driverapi.Create
|
|
|
|
case networkdb.DeleteEvent:
|
|
|
|
tname = event.Table
|
|
|
|
key = event.Key
|
|
|
|
value = event.Value
|
|
|
|
etype = driverapi.Delete
|
|
|
|
case networkdb.UpdateEvent:
|
|
|
|
tname = event.Table
|
|
|
|
key = event.Key
|
|
|
|
value = event.Value
|
|
|
|
etype = driverapi.Delete
|
|
|
|
}
|
|
|
|
|
|
|
|
d.EventNotify(etype, n.ID(), tname, key, value)
|
|
|
|
}
|
|
|
|
|
2017-01-31 17:13:08 +00:00
|
|
|
func (c *controller) handleNodeTableEvent(ev events.Event) {
|
|
|
|
var (
|
|
|
|
value []byte
|
|
|
|
isAdd bool
|
|
|
|
nodeAddr networkdb.NodeAddr
|
|
|
|
)
|
|
|
|
switch event := ev.(type) {
|
|
|
|
case networkdb.CreateEvent:
|
|
|
|
value = event.Value
|
|
|
|
isAdd = true
|
|
|
|
case networkdb.DeleteEvent:
|
|
|
|
value = event.Value
|
|
|
|
case networkdb.UpdateEvent:
|
|
|
|
logrus.Errorf("Unexpected update node table event = %#v", event)
|
|
|
|
}
|
|
|
|
|
|
|
|
err := json.Unmarshal(value, &nodeAddr)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Error unmarshalling node table event %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.processNodeDiscovery([]net.IP{nodeAddr.Addr}, isAdd)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
func (c *controller) handleEpTableEvent(ev events.Event) {
|
|
|
|
var (
|
2016-04-14 00:53:41 +00:00
|
|
|
nid string
|
|
|
|
eid string
|
2016-05-17 21:12:39 +00:00
|
|
|
value []byte
|
2016-03-30 21:42:58 +00:00
|
|
|
isAdd bool
|
2016-05-17 21:12:39 +00:00
|
|
|
epRec EndpointRecord
|
2016-03-30 21:42:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
switch event := ev.(type) {
|
|
|
|
case networkdb.CreateEvent:
|
2016-04-14 00:53:41 +00:00
|
|
|
nid = event.NetworkID
|
|
|
|
eid = event.Key
|
2016-05-17 21:12:39 +00:00
|
|
|
value = event.Value
|
2016-03-30 21:42:58 +00:00
|
|
|
isAdd = true
|
|
|
|
case networkdb.DeleteEvent:
|
2016-04-14 00:53:41 +00:00
|
|
|
nid = event.NetworkID
|
|
|
|
eid = event.Key
|
2016-05-17 21:12:39 +00:00
|
|
|
value = event.Value
|
2016-03-30 21:42:58 +00:00
|
|
|
case networkdb.UpdateEvent:
|
|
|
|
logrus.Errorf("Unexpected update service table event = %#v", event)
|
|
|
|
}
|
|
|
|
|
2016-04-14 00:53:41 +00:00
|
|
|
nw, err := c.NetworkByID(nid)
|
2016-03-30 21:42:58 +00:00
|
|
|
if err != nil {
|
2016-04-14 00:53:41 +00:00
|
|
|
logrus.Errorf("Could not find network %s while handling service table event: %v", nid, err)
|
2016-03-30 21:42:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
n := nw.(*network)
|
|
|
|
|
2016-05-17 21:12:39 +00:00
|
|
|
err = proto.Unmarshal(value, &epRec)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Errorf("Failed to unmarshal service table value: %v", err)
|
2016-03-30 21:42:58 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-05-17 21:12:39 +00:00
|
|
|
name := epRec.Name
|
|
|
|
svcName := epRec.ServiceName
|
|
|
|
svcID := epRec.ServiceID
|
2016-05-25 05:46:18 +00:00
|
|
|
vip := net.ParseIP(epRec.VirtualIP)
|
2016-05-17 21:12:39 +00:00
|
|
|
ip := net.ParseIP(epRec.EndpointIP)
|
2016-05-31 06:55:51 +00:00
|
|
|
ingressPorts := epRec.IngressPorts
|
2016-06-14 23:40:54 +00:00
|
|
|
aliases := epRec.Aliases
|
2016-08-03 03:28:33 +00:00
|
|
|
taskaliases := epRec.TaskAliases
|
2016-03-30 21:42:58 +00:00
|
|
|
|
|
|
|
if name == "" || ip == nil {
|
|
|
|
logrus.Errorf("Invalid endpoint name/ip received while handling service table event %s", value)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if isAdd {
|
2016-05-25 05:46:18 +00:00
|
|
|
if svcID != "" {
|
2016-06-14 23:40:54 +00:00
|
|
|
if err := c.addServiceBinding(svcName, svcID, nid, eid, vip, ingressPorts, aliases, ip); err != nil {
|
2016-05-25 05:46:18 +00:00
|
|
|
logrus.Errorf("Failed adding service binding for value %s: %v", value, err)
|
|
|
|
return
|
|
|
|
}
|
2016-04-14 00:53:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
n.addSvcRecords(name, ip, nil, true)
|
2016-08-03 03:28:33 +00:00
|
|
|
for _, alias := range taskaliases {
|
|
|
|
n.addSvcRecords(alias, ip, nil, true)
|
|
|
|
}
|
2016-03-30 21:42:58 +00:00
|
|
|
} else {
|
2016-05-25 05:46:18 +00:00
|
|
|
if svcID != "" {
|
2016-06-14 23:40:54 +00:00
|
|
|
if err := c.rmServiceBinding(svcName, svcID, nid, eid, vip, ingressPorts, aliases, ip); err != nil {
|
2016-05-25 05:46:18 +00:00
|
|
|
logrus.Errorf("Failed adding service binding for value %s: %v", value, err)
|
|
|
|
return
|
|
|
|
}
|
2016-04-14 00:53:41 +00:00
|
|
|
}
|
|
|
|
|
2016-03-30 21:42:58 +00:00
|
|
|
n.deleteSvcRecords(name, ip, nil, true)
|
2016-08-03 03:28:33 +00:00
|
|
|
for _, alias := range taskaliases {
|
|
|
|
n.deleteSvcRecords(alias, ip, nil, true)
|
|
|
|
}
|
2016-03-30 21:42:58 +00:00
|
|
|
}
|
|
|
|
}
|