From ca81f6ee7c74a3bf27dc9b044742961f4ef78094 Mon Sep 17 00:00:00 2001
From: Dong Chen <dongluo.chen@docker.com>
Date: Sat, 17 Sep 2016 23:30:39 -0700
Subject: [PATCH] dynamic service binding.

Signed-off-by: Dong Chen <dongluo.chen@docker.com>
---
 daemon/cluster/executor/backend.go            |  2 ++
 daemon/cluster/executor/container/adapter.go  |  8 +++++
 .../cluster/executor/container/controller.go  | 14 ++++++++
 daemon/container_operations.go                | 33 +++++++++++++++++++
 daemon/network.go                             |  4 +++
 5 files changed, 61 insertions(+)

diff --git a/daemon/cluster/executor/backend.go b/daemon/cluster/executor/backend.go
index fba4adec18..fb88613c1d 100644
--- a/daemon/cluster/executor/backend.go
+++ b/daemon/cluster/executor/backend.go
@@ -27,6 +27,8 @@ type Backend interface {
 	ContainerStart(name string, hostConfig *container.HostConfig, validateHostname bool, checkpoint string, checkpointDir string) error
 	ContainerStop(name string, seconds *int) error
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
+	ActivateContainerServiceBinding(containerName string) error
+	DeactivateContainerServiceBinding(containerName string) error
 	UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error
 	ContainerInspectCurrent(name string, size bool) (*types.ContainerJSON, error)
 	ContainerWaitWithContext(ctx context.Context, name string) error
diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go
index bd5745cc5b..618f4b22b4 100644
--- a/daemon/cluster/executor/container/adapter.go
+++ b/daemon/cluster/executor/container/adapter.go
@@ -331,6 +331,14 @@ func (c *containerAdapter) createVolumes(ctx context.Context) error {
 	return nil
 }
 
+func (c *containerAdapter) activateServiceBinding() error {
+	return c.backend.ActivateContainerServiceBinding(c.container.name())
+}
+
+func (c *containerAdapter) deactivateServiceBinding() error {
+	return c.backend.DeactivateContainerServiceBinding(c.container.name())
+}
+
 // todo: typed/wrapped errors
 func isContainerCreateNameConflict(err error) bool {
 	return strings.Contains(err.Error(), "Conflict. The name")
diff --git a/daemon/cluster/executor/container/controller.go b/daemon/cluster/executor/container/controller.go
index 7b8ed663e8..0185e415b5 100644
--- a/daemon/cluster/executor/container/controller.go
+++ b/daemon/cluster/executor/container/controller.go
@@ -183,6 +183,10 @@ func (r *controller) Start(ctx context.Context) error {
 
 	// no health check
 	if ctnr.Config == nil || ctnr.Config.Healthcheck == nil {
+		if err := r.adapter.activateServiceBinding(); err != nil {
+			log.G(ctx).WithError(err).Errorf("failed to activate service binding for container %s which has no healthcheck config", r.adapter.container.name())
+			return err
+		}
 		return nil
 	}
 
@@ -225,6 +229,10 @@ func (r *controller) Start(ctx context.Context) error {
 				// set health check error, and wait for container to fully exit ("die" event)
 				healthErr = ErrContainerUnhealthy
 			case "health_status: healthy":
+				if err := r.adapter.activateServiceBinding(); err != nil {
+					log.G(ctx).WithError(err).Errorf("failed to activate service binding for container %s after healthy event", r.adapter.container.name())
+					return err
+				}
 				return nil
 			}
 		case <-ctx.Done():
@@ -290,6 +298,12 @@ func (r *controller) Shutdown(ctx context.Context) error {
 		r.cancelPull()
 	}
 
+	// remove container from service binding
+	if err := r.adapter.deactivateServiceBinding(); err != nil {
+		log.G(ctx).WithError(err).Errorf("failed to deactivate service binding for container %s", r.adapter.container.name())
+		return err
+	}
+
 	if err := r.adapter.shutdown(ctx); err != nil {
 		if isUnknownContainer(err) || isStoppedContainer(err) {
 			return nil
diff --git a/daemon/container_operations.go b/daemon/container_operations.go
index ca6a377fec..66eb7965f4 100644
--- a/daemon/container_operations.go
+++ b/daemon/container_operations.go
@@ -719,6 +719,13 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
 		return err
 	}
 
+	if !container.Managed {
+		// add container name/alias to DNS
+		if err := daemon.ActivateContainerServiceBinding(container.Name); err != nil {
+			return fmt.Errorf("Activate container service binding for %s failed: %v", container.Name, err)
+		}
+	}
+
 	if err := container.UpdateJoinInfo(n, ep); err != nil {
 		return fmt.Errorf("Updating join info failed: %v", err)
 	}
@@ -987,3 +994,29 @@ func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, netw
 	}
 	return nil
 }
+
+// ActivateContainerServiceBinding puts this container into load balancer active rotation and DNS response
+func (daemon *Daemon) ActivateContainerServiceBinding(containerName string) error {
+	container, err := daemon.GetContainer(containerName)
+	if err != nil {
+		return err
+	}
+	sb := daemon.getNetworkSandbox(container)
+	if sb == nil {
+		return fmt.Errorf("network sandbox not exists for container %s", containerName)
+	}
+	return sb.EnableService()
+}
+
+// DeactivateContainerServiceBinding remove this container fromload balancer active rotation, and DNS response
+func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) error {
+	container, err := daemon.GetContainer(containerName)
+	if err != nil {
+		return err
+	}
+	sb := daemon.getNetworkSandbox(container)
+	if sb == nil {
+		return fmt.Errorf("network sandbox not exists for container %s", containerName)
+	}
+	return sb.DisableService()
+}
diff --git a/daemon/network.go b/daemon/network.go
index bde0bcdbff..a1ca959a34 100644
--- a/daemon/network.go
+++ b/daemon/network.go
@@ -178,6 +178,10 @@ func (daemon *Daemon) SetupIngress(create clustertypes.NetworkCreateRequest, nod
 		if err := ep.Join(sb, nil); err != nil {
 			logrus.Errorf("Failed joining ingress sandbox to ingress endpoint: %v", err)
 		}
+
+		if err := sb.EnableService(); err != nil {
+			logrus.WithError(err).Error("Failed enabling service for ingress sandbox")
+		}
 	}()
 
 	return nil