Browse Source

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 <mrjana@docker.com>
Jana Radhakrishnan 10 years ago
parent
commit
271bcd7ba1
3 changed files with 100 additions and 0 deletions
  1. 1 0
      libnetwork/controller.go
  2. 33 0
      libnetwork/endpoint.go
  3. 66 0
      libnetwork/network.go

+ 1 - 0
libnetwork/controller.go

@@ -284,6 +284,7 @@ func (c *controller) addNetwork(n *network) error {
 	}
 	}
 
 
 	n.Lock()
 	n.Lock()
+	n.svcRecords = svcMap{}
 	n.driver = dd.driver
 	n.driver = dd.driver
 	d := n.driver
 	d := n.driver
 	n.Unlock()
 	n.Unlock()

+ 33 - 0
libnetwork/endpoint.go

@@ -571,9 +571,39 @@ func (ep *endpoint) deleteEndpoint() error {
 		}
 		}
 		log.Warnf("driver error deleting endpoint %s : %v", name, err)
 		log.Warnf("driver error deleting endpoint %s : %v", name, err)
 	}
 	}
+
+	n.updateSvcRecord(ep, false)
 	return nil
 	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 {
 func (ep *endpoint) buildHostsFiles() error {
 	var extraContent []etchosts.Record
 	var extraContent []etchosts.Record
 
 
@@ -581,6 +611,7 @@ func (ep *endpoint) buildHostsFiles() error {
 	container := ep.container
 	container := ep.container
 	joinInfo := ep.joinInfo
 	joinInfo := ep.joinInfo
 	ifaces := ep.iFaces
 	ifaces := ep.iFaces
+	n := ep.network
 	ep.Unlock()
 	ep.Unlock()
 
 
 	if container == nil {
 	if container == nil {
@@ -613,6 +644,8 @@ func (ep *endpoint) buildHostsFiles() error {
 			etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
 			etchosts.Record{Hosts: extraHost.name, IP: extraHost.IP})
 	}
 	}
 
 
+	extraContent = append(extraContent, n.getSvcRecords()...)
+
 	IP := ""
 	IP := ""
 	if len(ifaces) != 0 && ifaces[0] != nil {
 	if len(ifaces) != 0 && ifaces[0] != nil {
 		IP = ifaces[0].addr.IP.String()
 		IP = ifaces[0].addr.IP.String()

+ 66 - 0
libnetwork/network.go

@@ -2,6 +2,7 @@ package libnetwork
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"net"
 	"sync"
 	"sync"
 
 
 	log "github.com/Sirupsen/logrus"
 	log "github.com/Sirupsen/logrus"
@@ -9,6 +10,7 @@ import (
 	"github.com/docker/libnetwork/config"
 	"github.com/docker/libnetwork/config"
 	"github.com/docker/libnetwork/datastore"
 	"github.com/docker/libnetwork/datastore"
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/driverapi"
+	"github.com/docker/libnetwork/etchosts"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/options"
 	"github.com/docker/libnetwork/options"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
@@ -51,6 +53,8 @@ type Network interface {
 // When the function returns true, the walk will stop.
 // When the function returns true, the walk will stop.
 type EndpointWalker func(ep Endpoint) bool
 type EndpointWalker func(ep Endpoint) bool
 
 
+type svcMap map[string]net.IP
+
 type network struct {
 type network struct {
 	ctrlr       *controller
 	ctrlr       *controller
 	name        string
 	name        string
@@ -62,6 +66,7 @@ type network struct {
 	endpoints   endpointTable
 	endpoints   endpointTable
 	generic     options.Generic
 	generic     options.Generic
 	dbIndex     uint64
 	dbIndex     uint64
+	svcRecords  svcMap
 	stopWatchCh chan struct{}
 	stopWatchCh chan struct{}
 	sync.Mutex
 	sync.Mutex
 }
 }
@@ -272,6 +277,8 @@ func (n *network) addEndpoint(ep *endpoint) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
+
+	n.updateSvcRecord(ep, true)
 	return nil
 	return nil
 }
 }
 
 
@@ -384,3 +391,62 @@ func (n *network) isGlobalScoped() (bool, error) {
 	n.Unlock()
 	n.Unlock()
 	return c.isDriverGlobalScoped(n.networkType)
 	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
+}