From 271bcd7ba17303edda91122adce61b1e568dee4e Mon Sep 17 00:00:00 2001 From: Jana Radhakrishnan Date: Thu, 18 Jun 2015 23:40:17 -0700 Subject: [PATCH] Service discovery Add a minimal service discover support using service names or service names qualified with network name. This is achieved by populating the container's /etc/hosts file record with the appropriate entries Signed-off-by: Jana Radhakrishnan --- libnetwork/controller.go | 1 + libnetwork/endpoint.go | 33 ++++++++++++++++++++ libnetwork/network.go | 66 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/libnetwork/controller.go b/libnetwork/controller.go index ae6f493168..b110f3b32c 100644 --- a/libnetwork/controller.go +++ b/libnetwork/controller.go @@ -284,6 +284,7 @@ func (c *controller) addNetwork(n *network) error { } n.Lock() + n.svcRecords = svcMap{} n.driver = dd.driver d := n.driver n.Unlock() diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index a4bce82a5b..72ab9a297b 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -571,9 +571,39 @@ func (ep *endpoint) deleteEndpoint() error { } log.Warnf("driver error deleting endpoint %s : %v", name, err) } + + n.updateSvcRecord(ep, false) return nil } +func (ep *endpoint) addHostEntries(recs []etchosts.Record) { + ep.Lock() + container := ep.container + ep.Unlock() + + if container == nil { + return + } + + if err := etchosts.Add(container.config.hostsPath, recs); err != nil { + log.Warnf("Failed adding service host entries to the running container: %v", err) + } +} + +func (ep *endpoint) deleteHostEntries(recs []etchosts.Record) { + ep.Lock() + container := ep.container + ep.Unlock() + + if container == nil { + return + } + + if err := etchosts.Delete(container.config.hostsPath, recs); err != nil { + log.Warnf("Failed deleting service host entries to the running container: %v", err) + } +} + func (ep *endpoint) buildHostsFiles() error { var extraContent []etchosts.Record @@ -581,6 +611,7 @@ func (ep *endpoint) buildHostsFiles() error { container := ep.container joinInfo := ep.joinInfo ifaces := ep.iFaces + n := ep.network ep.Unlock() if container == nil { @@ -613,6 +644,8 @@ func (ep *endpoint) buildHostsFiles() error { etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP}) } + extraContent = append(extraContent, n.getSvcRecords()...) + IP := "" if len(ifaces) != 0 && ifaces[0] != nil { IP = ifaces[0].addr.IP.String() diff --git a/libnetwork/network.go b/libnetwork/network.go index 1c48832ee7..de1bbb6684 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -2,6 +2,7 @@ package libnetwork import ( "encoding/json" + "net" "sync" log "github.com/Sirupsen/logrus" @@ -9,6 +10,7 @@ import ( "github.com/docker/libnetwork/config" "github.com/docker/libnetwork/datastore" "github.com/docker/libnetwork/driverapi" + "github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" "github.com/docker/libnetwork/types" @@ -51,6 +53,8 @@ type Network interface { // When the function returns true, the walk will stop. type EndpointWalker func(ep Endpoint) bool +type svcMap map[string]net.IP + type network struct { ctrlr *controller name string @@ -62,6 +66,7 @@ type network struct { endpoints endpointTable generic options.Generic dbIndex uint64 + svcRecords svcMap stopWatchCh chan struct{} sync.Mutex } @@ -272,6 +277,8 @@ func (n *network) addEndpoint(ep *endpoint) error { if err != nil { return err } + + n.updateSvcRecord(ep, true) return nil } @@ -384,3 +391,62 @@ func (n *network) isGlobalScoped() (bool, error) { n.Unlock() return c.isDriverGlobalScoped(n.networkType) } + +func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) { + n.Lock() + var recs []etchosts.Record + for _, iface := range ep.InterfaceList() { + if isAdd { + n.svcRecords[ep.Name()] = iface.Address().IP + n.svcRecords[ep.Name()+"."+n.name] = iface.Address().IP + } else { + delete(n.svcRecords, ep.Name()) + delete(n.svcRecords, ep.Name()+"."+n.name) + } + + recs = append(recs, etchosts.Record{ + Hosts: ep.Name(), + IP: iface.Address().IP.String(), + }) + + recs = append(recs, etchosts.Record{ + Hosts: ep.Name() + "." + n.name, + IP: iface.Address().IP.String(), + }) + } + n.Unlock() + + var epList []*endpoint + n.WalkEndpoints(func(e Endpoint) bool { + cEp := e.(*endpoint) + cEp.Lock() + if cEp.container != nil { + epList = append(epList, cEp) + } + cEp.Unlock() + return false + }) + + for _, cEp := range epList { + if isAdd { + cEp.addHostEntries(recs) + } else { + cEp.deleteHostEntries(recs) + } + } +} + +func (n *network) getSvcRecords() []etchosts.Record { + n.Lock() + defer n.Unlock() + + var recs []etchosts.Record + for h, ip := range n.svcRecords { + recs = append(recs, etchosts.Record{ + Hosts: h, + IP: ip.String(), + }) + } + + return recs +}