Skip to content

Commit ffdceda

Browse files
committed
Add service support
Add a notion of service in libnetwork so that a group of endpoints which form a service can be treated as such so that service level features can be added on top. Initially as part of this PR the support to assign a name to the said service is added which results in DNS queries to the service name to return all the IPs of the backing endpoints so that DNS RR behavior on the service name can be achieved. Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
1 parent 63cc2ec commit ffdceda

File tree

8 files changed

+174
-38
lines changed

8 files changed

+174
-38
lines changed

libnetwork/agent.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ func (ep *endpoint) addToCluster() error {
165165

166166
c := n.getController()
167167
if !ep.isAnonymous() && ep.Iface().Address() != nil {
168-
if err := c.agent.networkDB.CreateEntry("endpoint_table", n.ID(), ep.ID(), []byte(fmt.Sprintf("%s=%s", ep.Name(), ep.Iface().Address().IP))); err != nil {
168+
if err := c.addServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), ep.Iface().Address().IP); err != nil {
169+
return err
170+
}
171+
172+
if err := c.agent.networkDB.CreateEntry("endpoint_table", n.ID(), ep.ID(), []byte(fmt.Sprintf("%s,%s,%s,%s", ep.Name(), ep.svcName,
173+
ep.svcID, ep.Iface().Address().IP))); err != nil {
169174
return err
170175
}
171176
}
@@ -187,6 +192,12 @@ func (ep *endpoint) deleteFromCluster() error {
187192

188193
c := n.getController()
189194
if !ep.isAnonymous() {
195+
if ep.Iface().Address() != nil {
196+
if err := c.rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), ep.Iface().Address().IP); err != nil {
197+
return err
198+
}
199+
}
200+
190201
if err := c.agent.networkDB.DeleteEntry("endpoint_table", n.ID(), ep.ID()); err != nil {
191202
return err
192203
}
@@ -297,47 +308,62 @@ func (n *network) handleDriverTableEvent(ev events.Event) {
297308

298309
func (c *controller) handleEpTableEvent(ev events.Event) {
299310
var (
300-
id string
311+
nid string
312+
eid string
301313
value string
302314
isAdd bool
303315
)
304316

305317
switch event := ev.(type) {
306318
case networkdb.CreateEvent:
307-
id = event.NetworkID
319+
nid = event.NetworkID
320+
eid = event.Key
308321
value = string(event.Value)
309322
isAdd = true
310323
case networkdb.DeleteEvent:
311-
id = event.NetworkID
324+
nid = event.NetworkID
325+
eid = event.Key
312326
value = string(event.Value)
313327
case networkdb.UpdateEvent:
314328
logrus.Errorf("Unexpected update service table event = %#v", event)
315329
}
316330

317-
nw, err := c.NetworkByID(id)
331+
nw, err := c.NetworkByID(nid)
318332
if err != nil {
319-
logrus.Errorf("Could not find network %s while handling service table event: %v", id, err)
333+
logrus.Errorf("Could not find network %s while handling service table event: %v", nid, err)
320334
return
321335
}
322336
n := nw.(*network)
323337

324-
pair := strings.Split(value, "=")
325-
if len(pair) < 2 {
338+
vals := strings.Split(value, ",")
339+
if len(vals) < 4 {
326340
logrus.Errorf("Incorrect service table value = %s", value)
327341
return
328342
}
329343

330-
name := pair[0]
331-
ip := net.ParseIP(pair[1])
344+
name := vals[0]
345+
svcName := vals[1]
346+
svcID := vals[2]
347+
ip := net.ParseIP(vals[3])
332348

333349
if name == "" || ip == nil {
334350
logrus.Errorf("Invalid endpoint name/ip received while handling service table event %s", value)
335351
return
336352
}
337353

338354
if isAdd {
355+
if err := c.addServiceBinding(svcName, svcID, nid, eid, ip); err != nil {
356+
logrus.Errorf("Failed adding service binding for value %s: %v", value, err)
357+
return
358+
}
359+
339360
n.addSvcRecords(name, ip, nil, true)
340361
} else {
362+
if err := c.rmServiceBinding(svcName, svcID, nid, eid, ip); err != nil {
363+
logrus.Errorf("Failed adding service binding for value %s: %v", value, err)
364+
return
365+
}
366+
341367
n.deleteSvcRecords(name, ip, nil, true)
342368
}
343369
}

libnetwork/controller.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -123,20 +123,21 @@ type SandboxWalker func(sb Sandbox) bool
123123
type sandboxTable map[string]*sandbox
124124

125125
type controller struct {
126-
id string
127-
drvRegistry *drvregistry.DrvRegistry
128-
sandboxes sandboxTable
129-
cfg *config.Config
130-
stores []datastore.DataStore
131-
discovery hostdiscovery.HostDiscovery
132-
extKeyListener net.Listener
133-
watchCh chan *endpoint
134-
unWatchCh chan *endpoint
135-
svcDb map[string]svcInfo
136-
nmap map[string]*netWatch
137-
defOsSbox osl.Sandbox
138-
sboxOnce sync.Once
139-
agent *agent
126+
id string
127+
drvRegistry *drvregistry.DrvRegistry
128+
sandboxes sandboxTable
129+
cfg *config.Config
130+
stores []datastore.DataStore
131+
discovery hostdiscovery.HostDiscovery
132+
extKeyListener net.Listener
133+
watchCh chan *endpoint
134+
unWatchCh chan *endpoint
135+
svcRecords map[string]svcInfo
136+
nmap map[string]*netWatch
137+
serviceBindings map[string]*service
138+
defOsSbox osl.Sandbox
139+
sboxOnce sync.Once
140+
agent *agent
140141
sync.Mutex
141142
}
142143

@@ -148,10 +149,11 @@ type initializer struct {
148149
// New creates a new instance of network controller.
149150
func New(cfgOptions ...config.Option) (NetworkController, error) {
150151
c := &controller{
151-
id: stringid.GenerateRandomID(),
152-
cfg: config.ParseConfigOptions(cfgOptions...),
153-
sandboxes: sandboxTable{},
154-
svcDb: make(map[string]svcInfo),
152+
id: stringid.GenerateRandomID(),
153+
cfg: config.ParseConfigOptions(cfgOptions...),
154+
sandboxes: sandboxTable{},
155+
svcRecords: make(map[string]svcInfo),
156+
serviceBindings: make(map[string]*service),
155157
}
156158

157159
if err := c.agentInit(c.cfg.Daemon.Bind); err != nil {

libnetwork/endpoint.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ type endpoint struct {
6767
ipamOptions map[string]string
6868
aliases map[string]string
6969
myAliases []string
70+
svcID string
71+
svcName string
7072
dbIndex uint64
7173
dbExists bool
7274
sync.Mutex
@@ -89,6 +91,9 @@ func (ep *endpoint) MarshalJSON() ([]byte, error) {
8991
epMap["anonymous"] = ep.anonymous
9092
epMap["disableResolution"] = ep.disableResolution
9193
epMap["myAliases"] = ep.myAliases
94+
epMap["svcName"] = ep.svcName
95+
epMap["svcID"] = ep.svcID
96+
9297
return json.Marshal(epMap)
9398
}
9499

@@ -172,6 +177,15 @@ func (ep *endpoint) UnmarshalJSON(b []byte) (err error) {
172177
if l, ok := epMap["locator"]; ok {
173178
ep.locator = l.(string)
174179
}
180+
181+
if sn, ok := epMap["svcName"]; ok {
182+
ep.svcName = sn.(string)
183+
}
184+
185+
if si, ok := epMap["svcID"]; ok {
186+
ep.svcID = si.(string)
187+
}
188+
175189
ma, _ := json.Marshal(epMap["myAliases"])
176190
var myAliases []string
177191
json.Unmarshal(ma, &myAliases)
@@ -196,6 +210,8 @@ func (ep *endpoint) CopyTo(o datastore.KVObject) error {
196210
dstEp.dbExists = ep.dbExists
197211
dstEp.anonymous = ep.anonymous
198212
dstEp.disableResolution = ep.disableResolution
213+
dstEp.svcName = ep.svcName
214+
dstEp.svcID = ep.svcID
199215

200216
if ep.iface != nil {
201217
dstEp.iface = &endpointInterface{}
@@ -413,7 +429,9 @@ func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error {
413429
}()
414430

415431
// Watch for service records
416-
n.getController().watchSvcRecord(ep)
432+
if !n.getController().cfg.Daemon.IsAgent {
433+
n.getController().watchSvcRecord(ep)
434+
}
417435

418436
address := ""
419437
if ip := ep.getFirstInterfaceAddress(); ip != nil {
@@ -738,7 +756,9 @@ func (ep *endpoint) Delete(force bool) error {
738756
}()
739757

740758
// unwatch for service records
741-
n.getController().unWatchSvcRecord(ep)
759+
if !n.getController().cfg.Daemon.IsAgent {
760+
n.getController().unWatchSvcRecord(ep)
761+
}
742762

743763
if err = ep.deleteEndpoint(force); err != nil && !force {
744764
return err
@@ -871,6 +891,14 @@ func CreateOptionAlias(name string, alias string) EndpointOption {
871891
}
872892
}
873893

894+
// CreateOptionService function returns an option setter for setting service binding configuration
895+
func CreateOptionService(name, id string) EndpointOption {
896+
return func(ep *endpoint) {
897+
ep.svcName = name
898+
ep.svcID = id
899+
}
900+
}
901+
874902
//CreateOptionMyAlias function returns an option setter for setting endpoint's self alias
875903
func CreateOptionMyAlias(alias string) EndpointOption {
876904
return func(ep *endpoint) {

libnetwork/network.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,14 +1002,14 @@ func (n *network) addSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMapUp
10021002
c := n.getController()
10031003
c.Lock()
10041004
defer c.Unlock()
1005-
sr, ok := c.svcDb[n.ID()]
1005+
sr, ok := c.svcRecords[n.ID()]
10061006
if !ok {
10071007
sr = svcInfo{
10081008
svcMap: make(map[string][]net.IP),
10091009
svcIPv6Map: make(map[string][]net.IP),
10101010
ipMap: make(map[string]string),
10111011
}
1012-
c.svcDb[n.ID()] = sr
1012+
c.svcRecords[n.ID()] = sr
10131013
}
10141014

10151015
if ipMapUpdate {
@@ -1029,7 +1029,7 @@ func (n *network) deleteSvcRecords(name string, epIP net.IP, epIPv6 net.IP, ipMa
10291029
c := n.getController()
10301030
c.Lock()
10311031
defer c.Unlock()
1032-
sr, ok := c.svcDb[n.ID()]
1032+
sr, ok := c.svcRecords[n.ID()]
10331033
if !ok {
10341034
return
10351035
}
@@ -1054,7 +1054,7 @@ func (n *network) getSvcRecords(ep *endpoint) []etchosts.Record {
10541054
defer n.Unlock()
10551055

10561056
var recs []etchosts.Record
1057-
sr, _ := n.ctrlr.svcDb[n.id]
1057+
sr, _ := n.ctrlr.svcRecords[n.id]
10581058

10591059
for h, ip := range sr.svcMap {
10601060
if ep != nil && strings.Split(h, ".")[0] == ep.Name() {

libnetwork/sandbox.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ func (sb *sandbox) ResolveIP(ip string) string {
405405
for _, ep := range sb.getConnectedEndpoints() {
406406
n := ep.getNetwork()
407407

408-
sr, ok := n.getController().svcDb[n.ID()]
408+
sr, ok := n.getController().svcRecords[n.ID()]
409409
if !ok {
410410
continue
411411
}
@@ -512,7 +512,7 @@ func (sb *sandbox) resolveName(req string, networkName string, epList []*endpoin
512512
ep.Unlock()
513513
}
514514

515-
sr, ok := n.getController().svcDb[n.ID()]
515+
sr, ok := n.getController().svcRecords[n.ID()]
516516
if !ok {
517517
continue
518518
}

libnetwork/service.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package libnetwork
2+
3+
import "net"
4+
5+
type service struct {
6+
name string
7+
id string
8+
backEnds map[string]map[string]net.IP
9+
}
10+
11+
func newService(name string, id string) *service {
12+
return &service{
13+
name: name,
14+
id: id,
15+
backEnds: make(map[string]map[string]net.IP),
16+
}
17+
}
18+
19+
func (c *controller) addServiceBinding(name, sid, nid, eid string, ip net.IP) error {
20+
var s *service
21+
22+
n, err := c.NetworkByID(nid)
23+
if err != nil {
24+
return err
25+
}
26+
27+
c.Lock()
28+
s, ok := c.serviceBindings[sid]
29+
if !ok {
30+
s = newService(name, sid)
31+
}
32+
33+
netBackEnds, ok := s.backEnds[nid]
34+
if !ok {
35+
netBackEnds = make(map[string]net.IP)
36+
s.backEnds[nid] = netBackEnds
37+
}
38+
39+
netBackEnds[eid] = ip
40+
c.serviceBindings[sid] = s
41+
c.Unlock()
42+
43+
n.(*network).addSvcRecords(name, ip, nil, false)
44+
return nil
45+
}
46+
47+
func (c *controller) rmServiceBinding(name, sid, nid, eid string, ip net.IP) error {
48+
n, err := c.NetworkByID(nid)
49+
if err != nil {
50+
return err
51+
}
52+
53+
c.Lock()
54+
s, ok := c.serviceBindings[sid]
55+
if !ok {
56+
c.Unlock()
57+
return nil
58+
}
59+
60+
netBackEnds, ok := s.backEnds[nid]
61+
if !ok {
62+
c.Unlock()
63+
return nil
64+
}
65+
66+
delete(netBackEnds, eid)
67+
68+
if len(netBackEnds) == 0 {
69+
delete(s.backEnds, nid)
70+
}
71+
72+
if len(s.backEnds) == 0 {
73+
delete(c.serviceBindings, sid)
74+
}
75+
c.Unlock()
76+
77+
n.(*network).deleteSvcRecords(name, ip, nil, false)
78+
79+
return err
80+
}

libnetwork/store.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoi
420420

421421
// This is the last container going away for the network. Destroy
422422
// this network's svc db entry
423-
delete(c.svcDb, ep.getNetwork().ID())
423+
delete(c.svcRecords, ep.getNetwork().ID())
424424

425425
delete(nmap, ep.getNetwork().ID())
426426
}

libnetwork/test/integration/dnet/helpers.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ EOF
199199
cat ${tomlfile}
200200
docker run \
201201
-d \
202-
--hostname=${name} \
202+
--hostname=$(echo ${name} | sed s/_/-/g) \
203203
--name=${name} \
204204
--privileged \
205205
-p ${hport}:${cport} \

0 commit comments

Comments
 (0)