Преглед изворни кода

Make virtual service programming more robust

 - 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>
Alessandro Boch пре 8 година
родитељ
комит
4e69afc4f3

+ 6 - 0
libnetwork/ipvs/ipvs.go

@@ -42,6 +42,7 @@ type Destination struct {
 // Handle provides a namespace specific ipvs handle to program ipvs
 // rules.
 type Handle struct {
+	seq  uint32
 	sock *nl.NetlinkSocket
 }
 
@@ -82,6 +83,11 @@ func (i *Handle) NewService(s *Service) error {
 	return i.doCmd(s, nil, ipvsCmdNewService)
 }
 
+// IsServicePresent queries for the ipvs service in the passed handle.
+func (i *Handle) IsServicePresent(s *Service) bool {
+	return nil == i.doCmd(s, nil, ipvsCmdGetService)
+}
+
 // UpdateService updates an already existing service in the passed
 // handle.
 func (i *Handle) UpdateService(s *Service) error {

+ 3 - 1
libnetwork/ipvs/netlink.go

@@ -10,6 +10,7 @@ import (
 	"os/exec"
 	"strings"
 	"sync"
+	"sync/atomic"
 	"syscall"
 	"unsafe"
 
@@ -118,6 +119,7 @@ func fillDestinaton(d *Destination) nl.NetlinkRequestData {
 
 func (i *Handle) doCmd(s *Service, d *Destination, cmd uint8) error {
 	req := newIPVSRequest(cmd)
+	req.Seq = atomic.AddUint32(&i.seq, 1)
 	req.AddData(fillService(s))
 
 	if d != nil {
@@ -206,7 +208,7 @@ done:
 		}
 		for _, m := range msgs {
 			if m.Header.Seq != req.Seq {
-				return nil, fmt.Errorf("Wrong Seq nr %d, expected %d", m.Header.Seq, req.Seq)
+				continue
 			}
 			if m.Header.Pid != pid {
 				return nil, fmt.Errorf("Wrong pid %d, expected %d", m.Header.Pid, pid)

+ 1 - 11
libnetwork/service_common.go

@@ -61,11 +61,6 @@ func (c *controller) cleanupServiceBindings(cleanupNID string) {
 }
 
 func (c *controller) addServiceBinding(name, sid, nid, eid string, vip net.IP, ingressPorts []*PortConfig, aliases []string, ip net.IP) error {
-	var (
-		s          *service
-		addService bool
-	)
-
 	n, err := c.NetworkByID(nid)
 	if err != nil {
 		return err
@@ -123,11 +118,6 @@ func (c *controller) addServiceBinding(name, sid, nid, eid string, vip net.IP, i
 		fwMarkCtrMu.Unlock()
 
 		s.loadBalancers[nid] = lb
-
-		// Since we just created this load balancer make sure
-		// we add a new service service in IPVS rules.
-		addService = true
-
 	}
 
 	lb.backEnds[eid] = ip
@@ -135,7 +125,7 @@ func (c *controller) addServiceBinding(name, sid, nid, eid string, vip net.IP, i
 	// 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, addService)
+		n.(*network).addLBBackend(ip, vip, lb.fwMark, ingressPorts)
 	}
 
 	return nil

+ 6 - 10
libnetwork/service_linux.go

@@ -97,20 +97,16 @@ func (sb *sandbox) populateLoadbalancers(ep *endpoint) {
 		}
 
 		lb.service.Lock()
-		addService := true
 		for _, ip := range lb.backEnds {
-			sb.addLBBackend(ip, lb.vip, lb.fwMark, lb.service.ingressPorts,
-				eIP, gwIP, addService, n.ingress)
-			addService = false
+			sb.addLBBackend(ip, lb.vip, lb.fwMark, lb.service.ingressPorts, eIP, gwIP, n.ingress)
 		}
 		lb.service.Unlock()
 	}
 }
 
 // Add loadbalancer backend to all sandboxes which has a connection to
-// this network. If needed add the service as well, as specified by
-// the addService bool.
-func (n *network) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, addService bool) {
+// this network. If needed add the service as well.
+func (n *network) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig) {
 	n.WalkEndpoints(func(e Endpoint) bool {
 		ep := e.(*endpoint)
 		if sb, ok := ep.getSandbox(); ok {
@@ -123,7 +119,7 @@ func (n *network) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*Po
 				gwIP = ep.Iface().Address().IP
 			}
 
-			sb.addLBBackend(ip, vip, fwMark, ingressPorts, ep.Iface().Address(), gwIP, addService, n.ingress)
+			sb.addLBBackend(ip, vip, fwMark, ingressPorts, ep.Iface().Address(), gwIP, n.ingress)
 		}
 
 		return false
@@ -154,7 +150,7 @@ func (n *network) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*Por
 }
 
 // Add loadbalancer backend into one connected sandbox.
-func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, gwIP net.IP, addService bool, isIngressNetwork bool) {
+func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, gwIP net.IP, isIngressNetwork bool) {
 	if sb.osSbox == nil {
 		return
 	}
@@ -176,7 +172,7 @@ func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*P
 		SchedName:     ipvs.RoundRobin,
 	}
 
-	if addService {
+	if !i.IsServicePresent(s) {
 		var filteredPorts []*PortConfig
 		if sb.ingress {
 			filteredPorts = filterPortConfigs(ingressPorts, false)

+ 1 - 1
libnetwork/service_windows.go

@@ -2,7 +2,7 @@ package libnetwork
 
 import "net"
 
-func (n *network) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, addService bool) {
+func (n *network) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig) {
 }
 
 func (n *network) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, rmService bool) {