Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support service aliases that differ by container #1814

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net"
"sync"

mapset "github.com/deckarep/golang-set"
"github.com/docker/libnetwork/common"
)

Expand Down Expand Up @@ -47,9 +48,6 @@ type service struct {
// List of ingress ports exposed by the service
ingressPorts portConfigs

// Service aliases
aliases []string

// This maps tracks for each IP address the list of endpoints ID
// associated with it. At stable state the endpoint ID expected is 1
// but during transition and service change it is possible to have
Expand Down Expand Up @@ -79,6 +77,19 @@ func (s *service) printIPToEndpoint(ip string) (string, bool) {
return s.ipToEndpoint.String(ip)
}

// aliasSet returns the union of service aliases for all backends.
func (s *service) aliasSet() mapset.Set {
set := mapset.NewSet()
for _, lb := range s.loadBalancers {
for _, be := range lb.backEnds {
for _, alias := range be.serviceAliases {
set.Add(alias)
}
}
}
return set
}

type loadBalancer struct {
vip net.IP
fwMark uint32
Expand All @@ -92,7 +103,8 @@ type loadBalancer struct {
}

type loadBalancerBackend struct {
ip net.IP
containerName string
taskAliases []string
ip net.IP
containerName string
serviceAliases []string
taskAliases []string
}
113 changes: 71 additions & 42 deletions service_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ import (
"net"

"github.com/Sirupsen/logrus"
mapset "github.com/deckarep/golang-set"
"github.com/docker/libnetwork/common"
)

func (c *controller) addEndpointNameResolution(svcName, svcID, nID, eID, containerName string, vip net.IP, serviceAliases, taskAliases []string, ip net.IP, addService bool, method string) error {
func (c *controller) addEndpointNameResolution(svcName, svcID, nID, eID, containerName string, vip net.IP, serviceAliases, taskAliases []string, ip net.IP, method string) error {
n, err := c.NetworkByID(nID)
if err != nil {
return err
}

logrus.Debugf("addEndpointNameResolution %s %s add_service:%t", eID, svcName, addService)
logrus.Debugf("addEndpointNameResolution %s %s add_service", eID, svcName)

// Add container resolution mappings
c.addContainerNameResolution(nID, eID, containerName, taskAliases, ip, method)
Expand All @@ -27,18 +28,14 @@ func (c *controller) addEndpointNameResolution(svcName, svcID, nID, eID, contain
}

// Add service name to vip in DNS, if vip is valid. Otherwise resort to DNS RR
svcIP := vip
if len(vip) == 0 {
n.(*network).addSvcRecords(eID, svcName, ip, nil, false, method)
for _, alias := range serviceAliases {
n.(*network).addSvcRecords(eID, alias, ip, nil, false, method)
}
svcIP = ip
}

if addService && len(vip) != 0 {
n.(*network).addSvcRecords(eID, svcName, vip, nil, false, method)
for _, alias := range serviceAliases {
n.(*network).addSvcRecords(eID, alias, vip, nil, false, method)
}
n.(*network).addSvcRecords(eID, svcName, svcIP, nil, false, method)
for _, alias := range serviceAliases {
n.(*network).addSvcRecords(eID, alias, svcIP, nil, false, method)
}

return nil
Expand All @@ -62,39 +59,38 @@ func (c *controller) addContainerNameResolution(nID, eID, containerName string,
return nil
}

func (c *controller) deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName string, vip net.IP, serviceAliases, taskAliases []string, ip net.IP, rmService, multipleEntries bool, method string) error {
func (c *controller) deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName string, vip net.IP, serviceAliases, tasksServiceAliases, taskAliases []string, ip net.IP, rmService, multipleEntries bool, method string) error {
n, err := c.NetworkByID(nID)
if err != nil {
return err
}

logrus.Debugf("deleteEndpointNameResolution %s %s rm_service:%t suppress:%t", eID, svcName, rmService, multipleEntries)

// Delete container resolution mappings
c.delContainerNameResolution(nID, eID, containerName, taskAliases, ip, method)

if multipleEntries {
return nil
}

// Delete the special "tasks.svc_name" backend record.
if !multipleEntries {
n.(*network).deleteSvcRecords(eID, "tasks."+svcName, ip, nil, false, method)
for _, alias := range serviceAliases {
n.(*network).deleteSvcRecords(eID, "tasks."+alias, ip, nil, false, method)
}
n.(*network).deleteSvcRecords(eID, "tasks."+svcName, ip, nil, false, method)
for _, alias := range tasksServiceAliases {
n.(*network).deleteSvcRecords(eID, "tasks."+alias, ip, nil, false, method)
}

// If we are doing DNS RR delete the endpoint IP from DNS record right away.
if !multipleEntries && len(vip) == 0 {
n.(*network).deleteSvcRecords(eID, svcName, ip, nil, false, method)
for _, alias := range serviceAliases {
n.(*network).deleteSvcRecords(eID, alias, ip, nil, false, method)
}
svcIP := vip
if len(vip) == 0 {
svcIP = ip
}

// Remove the DNS record for VIP only if we are removing the service
if rmService && len(vip) != 0 && !multipleEntries {
n.(*network).deleteSvcRecords(eID, svcName, vip, nil, false, method)
for _, alias := range serviceAliases {
n.(*network).deleteSvcRecords(eID, alias, vip, nil, false, method)
}
if rmService {
n.(*network).deleteSvcRecords(eID, svcName, svcIP, nil, false, method)
}

for _, alias := range serviceAliases {
n.(*network).deleteSvcRecords(eID, alias, svcIP, nil, false, method)
}

return nil
Expand All @@ -118,13 +114,12 @@ func (c *controller) delContainerNameResolution(nID, eID, containerName string,
return nil
}

func newService(name string, id string, ingressPorts []*PortConfig, serviceAliases []string) *service {
func newService(name string, id string, ingressPorts []*PortConfig) *service {
return &service{
name: name,
id: id,
ingressPorts: ingressPorts,
loadBalancers: make(map[string]*loadBalancer),
aliases: serviceAliases,
ipToEndpoint: common.NewSetMatrix(),
}
}
Expand Down Expand Up @@ -180,7 +175,7 @@ func (c *controller) cleanupServiceBindings(cleanupNID string) {

cleanupFuncs = append(cleanupFuncs, func() {
if err := c.rmServiceBinding(service.name, service.id, networkID, epID, be.containerName, loadBalancer.vip,
service.ingressPorts, service.aliases, be.taskAliases, epIP, "cleanupServiceBindings"); err != nil {
service.ingressPorts, be.serviceAliases, be.taskAliases, epIP, "cleanupServiceBindings"); err != nil {
logrus.Errorf("Failed to remove service bindings for service %s network %s endpoint %s while cleanup: %v",
service.id, networkID, epID, err)
}
Expand All @@ -197,8 +192,6 @@ func (c *controller) cleanupServiceBindings(cleanupNID string) {
}

func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases, taskAliases []string, ip net.IP, method string) error {
var addService bool

n, err := c.NetworkByID(nID)
if err != nil {
return err
Expand All @@ -217,7 +210,7 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
if !ok {
// Create a new service if we are seeing this service
// for the first time.
s = newService(svcName, svcID, ingressPorts, serviceAliases)
s = newService(svcName, svcID, ingressPorts)
c.serviceBindings[skey] = s
}
c.Unlock()
Expand Down Expand Up @@ -250,12 +243,12 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
fwMarkCtrMu.Unlock()

s.loadBalancers[nID] = lb
addService = true
}

lb.backEnds[eID] = loadBalancerBackend{ip: ip,
containerName: containerName,
taskAliases: taskAliases}
containerName: containerName,
serviceAliases: serviceAliases,
taskAliases: taskAliases}

ok, entries := s.assignIPToEndpoint(ip.String(), eID)
if !ok || entries > 1 {
Expand All @@ -270,15 +263,14 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
}

// Add the appropriate name resolutions
c.addEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, addService, "addServiceBinding")
c.addEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, "addServiceBinding")

logrus.Debugf("addServiceBinding from %s END for %s %s", method, svcName, eID)

return nil
}

func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases []string, taskAliases []string, ip net.IP, method string) error {

var rmService bool

n, err := c.NetworkByID(nID)
Expand Down Expand Up @@ -308,7 +300,7 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st
return nil
}

_, ok = lb.backEnds[eID]
be, ok := lb.backEnds[eID]
if !ok {
logrus.Warnf("rmServiceBinding %s %s %s aborted lb.backEnds[eid] !ok", method, svcName, eID)
return nil
Expand Down Expand Up @@ -347,8 +339,45 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st
n.(*network).rmLBBackend(ip, vip, lb.fwMark, ingressPorts, rmService)
}

// Always clean up all possible aliases for the special "tasks.<alias>" names.
tasksServiceAliases := serviceAliases
logrus.Debugf("tasksServiceAliases %s", tasksServiceAliases)

if len(vip) == 0 {
// If not using a VIP then always remove the service and all its aliases.
rmService = true
} else if !rmService {
// Make sure to only remove the VIP when the last alias referencing it has
// been removed, but remove any service aliases that are no longer needed.
epAliasSet := mapset.NewSet()
for _, alias := range serviceAliases {
epAliasSet.Add(alias)
}

// This had better be redundant but just in case...
for _, alias := range be.serviceAliases {
epAliasSet.Add(alias)
}

logrus.Debugf("epAliasSet %s", epAliasSet)

sAliasSet := s.aliasSet()
logrus.Debugf("sAliasSet %s", sAliasSet)

for alias := range sAliasSet.Iter() {
epAliasSet.Remove(alias)
}

serviceAliases = make([]string, 0, epAliasSet.Cardinality())
for alias := range epAliasSet.Iter() {
serviceAliases = append(serviceAliases, alias.(string))
}
}

logrus.Debugf("serviceAliases %s", serviceAliases)

// Delete the name resolutions
c.deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, rmService, entries > 0, "rmServiceBinding")
c.deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, tasksServiceAliases, taskAliases, ip, rmService, entries > 0, "rmServiceBinding")

logrus.Debugf("rmServiceBinding from %s END for %s %s", method, svcName, eID)
return nil
Expand Down