mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
4e69afc4f3
- Do not relay on software flags to decide when to create the virtual service. Instead query the kernel for presence. So that it cannot happen that a real server creation fails because the virtual server is missing. Signed-off-by: Alessandro Boch <aboch@docker.com>
216 lines
4.9 KiB
Go
216 lines
4.9 KiB
Go
// +build linux windows
|
|
|
|
package libnetwork
|
|
|
|
import (
|
|
"net"
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
)
|
|
|
|
func newService(name string, id string, ingressPorts []*PortConfig, aliases []string) *service {
|
|
return &service{
|
|
name: name,
|
|
id: id,
|
|
ingressPorts: ingressPorts,
|
|
loadBalancers: make(map[string]*loadBalancer),
|
|
aliases: aliases,
|
|
}
|
|
}
|
|
|
|
func (c *controller) cleanupServiceBindings(cleanupNID string) {
|
|
var cleanupFuncs []func()
|
|
|
|
c.Lock()
|
|
services := make([]*service, 0, len(c.serviceBindings))
|
|
for _, s := range c.serviceBindings {
|
|
services = append(services, s)
|
|
}
|
|
c.Unlock()
|
|
|
|
for _, s := range services {
|
|
s.Lock()
|
|
for nid, lb := range s.loadBalancers {
|
|
if cleanupNID != "" && nid != cleanupNID {
|
|
continue
|
|
}
|
|
|
|
for eid, ip := range lb.backEnds {
|
|
service := s
|
|
loadBalancer := lb
|
|
networkID := nid
|
|
epID := eid
|
|
epIP := ip
|
|
|
|
cleanupFuncs = append(cleanupFuncs, func() {
|
|
if err := c.rmServiceBinding(service.name, service.id, networkID, epID, loadBalancer.vip,
|
|
service.ingressPorts, service.aliases, epIP); err != nil {
|
|
logrus.Errorf("Failed to remove service bindings for service %s network %s endpoint %s while cleanup: %v",
|
|
service.id, networkID, epID, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
s.Unlock()
|
|
}
|
|
|
|
for _, f := range cleanupFuncs {
|
|
f()
|
|
}
|
|
|
|
}
|
|
|
|
func (c *controller) addServiceBinding(name, sid, nid, eid string, vip net.IP, ingressPorts []*PortConfig, aliases []string, ip net.IP) error {
|
|
n, err := c.NetworkByID(nid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
skey := serviceKey{
|
|
id: sid,
|
|
ports: portConfigs(ingressPorts).String(),
|
|
}
|
|
|
|
c.Lock()
|
|
s, ok := c.serviceBindings[skey]
|
|
if !ok {
|
|
// Create a new service if we are seeing this service
|
|
// for the first time.
|
|
s = newService(name, sid, ingressPorts, aliases)
|
|
c.serviceBindings[skey] = s
|
|
}
|
|
c.Unlock()
|
|
|
|
// Add endpoint IP to special "tasks.svc_name" so that the
|
|
// applications have access to DNS RR.
|
|
n.(*network).addSvcRecords("tasks."+name, ip, nil, false)
|
|
for _, alias := range aliases {
|
|
n.(*network).addSvcRecords("tasks."+alias, ip, nil, false)
|
|
}
|
|
|
|
// Add service name to vip in DNS, if vip is valid. Otherwise resort to DNS RR
|
|
svcIP := vip
|
|
if len(svcIP) == 0 {
|
|
svcIP = ip
|
|
}
|
|
n.(*network).addSvcRecords(name, svcIP, nil, false)
|
|
for _, alias := range aliases {
|
|
n.(*network).addSvcRecords(alias, svcIP, nil, false)
|
|
}
|
|
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
lb, ok := s.loadBalancers[nid]
|
|
if !ok {
|
|
// Create a new load balancer if we are seeing this
|
|
// network attachment on the service for the first
|
|
// time.
|
|
lb = &loadBalancer{
|
|
vip: vip,
|
|
fwMark: fwMarkCtr,
|
|
backEnds: make(map[string]net.IP),
|
|
service: s,
|
|
}
|
|
|
|
fwMarkCtrMu.Lock()
|
|
fwMarkCtr++
|
|
fwMarkCtrMu.Unlock()
|
|
|
|
s.loadBalancers[nid] = lb
|
|
}
|
|
|
|
lb.backEnds[eid] = ip
|
|
|
|
// Add loadbalancer service and backend in all sandboxes in
|
|
// the network only if vip is valid.
|
|
if len(vip) != 0 {
|
|
n.(*network).addLBBackend(ip, vip, lb.fwMark, ingressPorts)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *controller) rmServiceBinding(name, sid, nid, eid string, vip net.IP, ingressPorts []*PortConfig, aliases []string, ip net.IP) error {
|
|
var rmService bool
|
|
|
|
n, err := c.NetworkByID(nid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
skey := serviceKey{
|
|
id: sid,
|
|
ports: portConfigs(ingressPorts).String(),
|
|
}
|
|
|
|
c.Lock()
|
|
s, ok := c.serviceBindings[skey]
|
|
c.Unlock()
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
s.Lock()
|
|
lb, ok := s.loadBalancers[nid]
|
|
if !ok {
|
|
s.Unlock()
|
|
return nil
|
|
}
|
|
|
|
_, ok = lb.backEnds[eid]
|
|
if !ok {
|
|
s.Unlock()
|
|
return nil
|
|
}
|
|
|
|
delete(lb.backEnds, eid)
|
|
if len(lb.backEnds) == 0 {
|
|
// All the backends for this service have been
|
|
// removed. Time to remove the load balancer and also
|
|
// remove the service entry in IPVS.
|
|
rmService = true
|
|
|
|
delete(s.loadBalancers, nid)
|
|
}
|
|
|
|
if len(s.loadBalancers) == 0 {
|
|
// All loadbalancers for the service removed. Time to
|
|
// remove the service itself.
|
|
c.Lock()
|
|
delete(c.serviceBindings, skey)
|
|
c.Unlock()
|
|
}
|
|
|
|
// Remove loadbalancer service(if needed) and backend in all
|
|
// sandboxes in the network only if the vip is valid.
|
|
if len(vip) != 0 {
|
|
n.(*network).rmLBBackend(ip, vip, lb.fwMark, ingressPorts, rmService)
|
|
}
|
|
s.Unlock()
|
|
|
|
// Delete the special "tasks.svc_name" backend record.
|
|
n.(*network).deleteSvcRecords("tasks."+name, ip, nil, false)
|
|
for _, alias := range aliases {
|
|
n.(*network).deleteSvcRecords("tasks."+alias, ip, nil, false)
|
|
}
|
|
|
|
// If we are doing DNS RR add the endpoint IP to DNS record
|
|
// right away.
|
|
if len(vip) == 0 {
|
|
n.(*network).deleteSvcRecords(name, ip, nil, false)
|
|
for _, alias := range aliases {
|
|
n.(*network).deleteSvcRecords(alias, ip, nil, false)
|
|
}
|
|
}
|
|
|
|
// Remove the DNS record for VIP only if we are removing the service
|
|
if rmService && len(vip) != 0 {
|
|
n.(*network).deleteSvcRecords(name, vip, nil, false)
|
|
for _, alias := range aliases {
|
|
n.(*network).deleteSvcRecords(alias, vip, nil, false)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|