Browse code

Import libnetwork fix for rolling updates

This patch allows endpoints to complete servicing connections while
being removed from a service. The fix is entirely within libnetwork
and requires no changes to the moby codebase proper. It operates
by initially down-weighting a container endpoint in the load balancer
to 0 while keeping the endpoint present in the load balancer. This
allows traffic to continue to flow to the endpoint while preventing new
connections from going to the endpoint. This allows the container
to complete requests during the "stop_grace_period" and then exit when
finished without interruption of service.

This change requires propagating the status of disabled service
endpoints via the networkDB. Accordingly, the patch includes both code
to generate and handle service update messages. It also augments the
service structure with a ServiceDisabled boolean to convey whether an
endpoint should ultimately be removed or just disabled. This,
naturally, required a rebuild of the protocol buffer code.

The protocol buffer encoding is designed to support additions of fields
to messages in a backwards-compatible manner. Protocol buffer
unmarshalling code automatically skips past any fields that it isn't
aware of. As a result, an older moby daemon without this fix can
receive and will process correctly networkDB messages from newer moby
daemons with this patch.

As it turns out, the additional field is simply a bool that is otherwise
irrelevent on networkDB create and delete events. So its absence in
older moby daemon processing has no impact. However, the fix leverages
the "update" networkDB message which was previously unused in
libnetwork. Although older libnetwork implementations parse the message
cleanly, they will see the message as unexpected and as such issue a log
at error level indicating the receipt of such.

Other than this there should be no other negative impact for use of this
patch in mixed environments. (Although older mobys won't be able to
gracefully downgrade connections on their nodes of course.)

Signed-off-by: Chris Telfer <ctelfer@docker.com>

Chris Telfer authored on 2018/03/20 03:57:37
Showing 10 changed files
... ...
@@ -32,7 +32,7 @@ github.com/tonistiigi/fsutil dea3a0da73aee887fc02142d995be764106ac5e2
32 32
 #get libnetwork packages
33 33
 
34 34
 # When updating, also update LIBNETWORK_COMMIT in hack/dockerfile/install/proxy accordingly
35
-github.com/docker/libnetwork 1b91bc94094ecfdae41daa465cc0c8df37dfb3dd
35
+github.com/docker/libnetwork 2bf63300c52f5ea61989f85c732f00097d746530
36 36
 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
37 37
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
38 38
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -638,14 +638,15 @@ func (ep *endpoint) addServiceInfoToCluster(sb *sandbox) error {
638 638
 	}
639 639
 
640 640
 	buf, err := proto.Marshal(&EndpointRecord{
641
-		Name:         name,
642
-		ServiceName:  ep.svcName,
643
-		ServiceID:    ep.svcID,
644
-		VirtualIP:    ep.virtualIP.String(),
645
-		IngressPorts: ingressPorts,
646
-		Aliases:      ep.svcAliases,
647
-		TaskAliases:  ep.myAliases,
648
-		EndpointIP:   ep.Iface().Address().IP.String(),
641
+		Name:            name,
642
+		ServiceName:     ep.svcName,
643
+		ServiceID:       ep.svcID,
644
+		VirtualIP:       ep.virtualIP.String(),
645
+		IngressPorts:    ingressPorts,
646
+		Aliases:         ep.svcAliases,
647
+		TaskAliases:     ep.myAliases,
648
+		EndpointIP:      ep.Iface().Address().IP.String(),
649
+		ServiceDisabled: false,
649 650
 	})
650 651
 	if err != nil {
651 652
 		return err
... ...
@@ -663,7 +664,7 @@ func (ep *endpoint) addServiceInfoToCluster(sb *sandbox) error {
663 663
 	return nil
664 664
 }
665 665
 
666
-func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, method string) error {
666
+func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, fullRemove bool, method string) error {
667 667
 	if ep.isAnonymous() && len(ep.myAliases) == 0 {
668 668
 		return nil
669 669
 	}
... ...
@@ -677,6 +678,15 @@ func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, method string) err
677 677
 	defer sb.Service.Unlock()
678 678
 	logrus.Debugf("deleteServiceInfoFromCluster from %s START for %s %s", method, ep.svcName, ep.ID())
679 679
 
680
+	// Avoid a race w/ with a container that aborts preemptively.  This would
681
+	// get caught in disableServceInNetworkDB, but we check here to make the
682
+	// nature of the condition more clear.
683
+	// See comment in addServiceInfoToCluster()
684
+	if e := sb.getEndpoint(ep.ID()); e == nil {
685
+		logrus.Warnf("deleteServiceInfoFromCluster suppressing service resolution ep is not anymore in the sandbox %s", ep.ID())
686
+		return nil
687
+	}
688
+
680 689
 	c := n.getController()
681 690
 	agent := c.getAgent()
682 691
 
... ...
@@ -686,9 +696,13 @@ func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, method string) err
686 686
 	}
687 687
 
688 688
 	if agent != nil {
689
-		// First delete from networkDB then locally
690
-		if err := agent.networkDB.DeleteEntry(libnetworkEPTable, n.ID(), ep.ID()); err != nil {
691
-			logrus.Warnf("deleteServiceInfoFromCluster NetworkDB DeleteEntry failed for %s %s err:%s", ep.id, n.id, err)
689
+		// First update the networkDB then locally
690
+		if fullRemove {
691
+			if err := agent.networkDB.DeleteEntry(libnetworkEPTable, n.ID(), ep.ID()); err != nil {
692
+				logrus.Warnf("deleteServiceInfoFromCluster NetworkDB DeleteEntry failed for %s %s err:%s", ep.id, n.id, err)
693
+			}
694
+		} else {
695
+			disableServiceInNetworkDB(agent, n, ep)
692 696
 		}
693 697
 	}
694 698
 
... ...
@@ -699,7 +713,7 @@ func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, method string) err
699 699
 			if n.ingress {
700 700
 				ingressPorts = ep.ingressPorts
701 701
 			}
702
-			if err := c.rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), name, ep.virtualIP, ingressPorts, ep.svcAliases, ep.myAliases, ep.Iface().Address().IP, "deleteServiceInfoFromCluster", true); err != nil {
702
+			if err := c.rmServiceBinding(ep.svcName, ep.svcID, n.ID(), ep.ID(), name, ep.virtualIP, ingressPorts, ep.svcAliases, ep.myAliases, ep.Iface().Address().IP, "deleteServiceInfoFromCluster", true, fullRemove); err != nil {
703 703
 				return err
704 704
 			}
705 705
 		} else {
... ...
@@ -715,6 +729,35 @@ func (ep *endpoint) deleteServiceInfoFromCluster(sb *sandbox, method string) err
715 715
 	return nil
716 716
 }
717 717
 
718
+func disableServiceInNetworkDB(a *agent, n *network, ep *endpoint) {
719
+	var epRec EndpointRecord
720
+
721
+	logrus.Debugf("disableServiceInNetworkDB for %s %s", ep.svcName, ep.ID())
722
+
723
+	// Update existing record to indicate that the service is disabled
724
+	inBuf, err := a.networkDB.GetEntry(libnetworkEPTable, n.ID(), ep.ID())
725
+	if err != nil {
726
+		logrus.Warnf("disableServiceInNetworkDB GetEntry failed for %s %s err:%s", ep.id, n.id, err)
727
+		return
728
+	}
729
+	// Should never fail
730
+	if err := proto.Unmarshal(inBuf, &epRec); err != nil {
731
+		logrus.Errorf("disableServiceInNetworkDB unmarshal failed for %s %s err:%s", ep.id, n.id, err)
732
+		return
733
+	}
734
+	epRec.ServiceDisabled = true
735
+	// Should never fail
736
+	outBuf, err := proto.Marshal(&epRec)
737
+	if err != nil {
738
+		logrus.Errorf("disableServiceInNetworkDB marshalling failed for %s %s err:%s", ep.id, n.id, err)
739
+		return
740
+	}
741
+	// Send update to the whole cluster
742
+	if err := a.networkDB.UpdateEntry(libnetworkEPTable, n.ID(), ep.ID(), outBuf); err != nil {
743
+		logrus.Warnf("disableServiceInNetworkDB UpdateEntry failed for %s %s err:%s", ep.id, n.id, err)
744
+	}
745
+}
746
+
718 747
 func (n *network) addDriverWatches() {
719 748
 	if !n.isClusterEligible() {
720 749
 		return
... ...
@@ -844,7 +887,6 @@ func (c *controller) handleEpTableEvent(ev events.Event) {
844 844
 		nid   string
845 845
 		eid   string
846 846
 		value []byte
847
-		isAdd bool
848 847
 		epRec EndpointRecord
849 848
 	)
850 849
 
... ...
@@ -853,12 +895,15 @@ func (c *controller) handleEpTableEvent(ev events.Event) {
853 853
 		nid = event.NetworkID
854 854
 		eid = event.Key
855 855
 		value = event.Value
856
-		isAdd = true
857 856
 	case networkdb.DeleteEvent:
858 857
 		nid = event.NetworkID
859 858
 		eid = event.Key
860 859
 		value = event.Value
861 860
 	case networkdb.UpdateEvent:
861
+		nid = event.NetworkID
862
+		eid = event.Key
863
+		value = event.Value
864
+	default:
862 865
 		logrus.Errorf("Unexpected update service table event = %#v", event)
863 866
 		return
864 867
 	}
... ...
@@ -883,7 +928,8 @@ func (c *controller) handleEpTableEvent(ev events.Event) {
883 883
 		return
884 884
 	}
885 885
 
886
-	if isAdd {
886
+	switch ev.(type) {
887
+	case networkdb.CreateEvent:
887 888
 		logrus.Debugf("handleEpTableEvent ADD %s R:%v", eid, epRec)
888 889
 		if svcID != "" {
889 890
 			// This is a remote task part of a service
... ...
@@ -897,11 +943,12 @@ func (c *controller) handleEpTableEvent(ev events.Event) {
897 897
 				logrus.Errorf("failed adding container name resolution for %s epRec:%v err:%v", eid, epRec, err)
898 898
 			}
899 899
 		}
900
-	} else {
900
+
901
+	case networkdb.DeleteEvent:
901 902
 		logrus.Debugf("handleEpTableEvent DEL %s R:%v", eid, epRec)
902 903
 		if svcID != "" {
903 904
 			// This is a remote task part of a service
904
-			if err := c.rmServiceBinding(svcName, svcID, nid, eid, containerName, vip, ingressPorts, serviceAliases, taskAliases, ip, "handleEpTableEvent", true); err != nil {
905
+			if err := c.rmServiceBinding(svcName, svcID, nid, eid, containerName, vip, ingressPorts, serviceAliases, taskAliases, ip, "handleEpTableEvent", true, true); err != nil {
905 906
 				logrus.Errorf("failed removing service binding for %s epRec:%v err:%v", eid, epRec, err)
906 907
 				return
907 908
 			}
... ...
@@ -911,5 +958,18 @@ func (c *controller) handleEpTableEvent(ev events.Event) {
911 911
 				logrus.Errorf("failed removing container name resolution for %s epRec:%v err:%v", eid, epRec, err)
912 912
 			}
913 913
 		}
914
+	case networkdb.UpdateEvent:
915
+		logrus.Debugf("handleEpTableEvent UPD %s R:%v", eid, epRec)
916
+		// We currently should only get these to inform us that an endpoint
917
+		// is disabled.  Report if otherwise.
918
+		if svcID == "" || !epRec.ServiceDisabled {
919
+			logrus.Errorf("Unexpected update table event for %s epRec:%v", eid, epRec)
920
+			return
921
+		}
922
+		// This is a remote task that is part of a service that is now disabled
923
+		if err := c.rmServiceBinding(svcName, svcID, nid, eid, containerName, vip, ingressPorts, serviceAliases, taskAliases, ip, "handleEpTableEvent", true, false); err != nil {
924
+			logrus.Errorf("failed disabling service binding for %s epRec:%v err:%v", eid, epRec, err)
925
+			return
926
+		}
914 927
 	}
915 928
 }
... ...
@@ -77,6 +77,8 @@ type EndpointRecord struct {
77 77
 	Aliases []string `protobuf:"bytes,7,rep,name=aliases" json:"aliases,omitempty"`
78 78
 	// List of aliases task specific aliases
79 79
 	TaskAliases []string `protobuf:"bytes,8,rep,name=task_aliases,json=taskAliases" json:"task_aliases,omitempty"`
80
+	// Whether this enpoint's service has been disabled
81
+	ServiceDisabled bool `protobuf:"varint,9,opt,name=service_disabled,json=serviceDisabled,proto3" json:"service_disabled,omitempty"`
80 82
 }
81 83
 
82 84
 func (m *EndpointRecord) Reset()                    { *m = EndpointRecord{} }
... ...
@@ -139,6 +141,13 @@ func (m *EndpointRecord) GetTaskAliases() []string {
139 139
 	return nil
140 140
 }
141 141
 
142
+func (m *EndpointRecord) GetServiceDisabled() bool {
143
+	if m != nil {
144
+		return m.ServiceDisabled
145
+	}
146
+	return false
147
+}
148
+
142 149
 // PortConfig specifies an exposed port which can be
143 150
 // addressed using the given name. This can be later queried
144 151
 // using a service discovery api or a DNS SRV query. The node
... ...
@@ -202,7 +211,7 @@ func (this *EndpointRecord) GoString() string {
202 202
 	if this == nil {
203 203
 		return "nil"
204 204
 	}
205
-	s := make([]string, 0, 12)
205
+	s := make([]string, 0, 13)
206 206
 	s = append(s, "&libnetwork.EndpointRecord{")
207 207
 	s = append(s, "Name: "+fmt.Sprintf("%#v", this.Name)+",\n")
208 208
 	s = append(s, "ServiceName: "+fmt.Sprintf("%#v", this.ServiceName)+",\n")
... ...
@@ -214,6 +223,7 @@ func (this *EndpointRecord) GoString() string {
214 214
 	}
215 215
 	s = append(s, "Aliases: "+fmt.Sprintf("%#v", this.Aliases)+",\n")
216 216
 	s = append(s, "TaskAliases: "+fmt.Sprintf("%#v", this.TaskAliases)+",\n")
217
+	s = append(s, "ServiceDisabled: "+fmt.Sprintf("%#v", this.ServiceDisabled)+",\n")
217 218
 	s = append(s, "}")
218 219
 	return strings.Join(s, "")
219 220
 }
... ...
@@ -325,6 +335,16 @@ func (m *EndpointRecord) MarshalTo(dAtA []byte) (int, error) {
325 325
 			i += copy(dAtA[i:], s)
326 326
 		}
327 327
 	}
328
+	if m.ServiceDisabled {
329
+		dAtA[i] = 0x48
330
+		i++
331
+		if m.ServiceDisabled {
332
+			dAtA[i] = 1
333
+		} else {
334
+			dAtA[i] = 0
335
+		}
336
+		i++
337
+	}
328 338
 	return i, nil
329 339
 }
330 340
 
... ...
@@ -367,24 +387,6 @@ func (m *PortConfig) MarshalTo(dAtA []byte) (int, error) {
367 367
 	return i, nil
368 368
 }
369 369
 
370
-func encodeFixed64Agent(dAtA []byte, offset int, v uint64) int {
371
-	dAtA[offset] = uint8(v)
372
-	dAtA[offset+1] = uint8(v >> 8)
373
-	dAtA[offset+2] = uint8(v >> 16)
374
-	dAtA[offset+3] = uint8(v >> 24)
375
-	dAtA[offset+4] = uint8(v >> 32)
376
-	dAtA[offset+5] = uint8(v >> 40)
377
-	dAtA[offset+6] = uint8(v >> 48)
378
-	dAtA[offset+7] = uint8(v >> 56)
379
-	return offset + 8
380
-}
381
-func encodeFixed32Agent(dAtA []byte, offset int, v uint32) int {
382
-	dAtA[offset] = uint8(v)
383
-	dAtA[offset+1] = uint8(v >> 8)
384
-	dAtA[offset+2] = uint8(v >> 16)
385
-	dAtA[offset+3] = uint8(v >> 24)
386
-	return offset + 4
387
-}
388 370
 func encodeVarintAgent(dAtA []byte, offset int, v uint64) int {
389 371
 	for v >= 1<<7 {
390 372
 		dAtA[offset] = uint8(v&0x7f | 0x80)
... ...
@@ -435,6 +437,9 @@ func (m *EndpointRecord) Size() (n int) {
435 435
 			n += 1 + l + sovAgent(uint64(l))
436 436
 		}
437 437
 	}
438
+	if m.ServiceDisabled {
439
+		n += 2
440
+	}
438 441
 	return n
439 442
 }
440 443
 
... ...
@@ -483,6 +488,7 @@ func (this *EndpointRecord) String() string {
483 483
 		`IngressPorts:` + strings.Replace(fmt.Sprintf("%v", this.IngressPorts), "PortConfig", "PortConfig", 1) + `,`,
484 484
 		`Aliases:` + fmt.Sprintf("%v", this.Aliases) + `,`,
485 485
 		`TaskAliases:` + fmt.Sprintf("%v", this.TaskAliases) + `,`,
486
+		`ServiceDisabled:` + fmt.Sprintf("%v", this.ServiceDisabled) + `,`,
486 487
 		`}`,
487 488
 	}, "")
488 489
 	return s
... ...
@@ -771,6 +777,26 @@ func (m *EndpointRecord) Unmarshal(dAtA []byte) error {
771 771
 			}
772 772
 			m.TaskAliases = append(m.TaskAliases, string(dAtA[iNdEx:postIndex]))
773 773
 			iNdEx = postIndex
774
+		case 9:
775
+			if wireType != 0 {
776
+				return fmt.Errorf("proto: wrong wireType = %d for field ServiceDisabled", wireType)
777
+			}
778
+			var v int
779
+			for shift := uint(0); ; shift += 7 {
780
+				if shift >= 64 {
781
+					return ErrIntOverflowAgent
782
+				}
783
+				if iNdEx >= l {
784
+					return io.ErrUnexpectedEOF
785
+				}
786
+				b := dAtA[iNdEx]
787
+				iNdEx++
788
+				v |= (int(b) & 0x7F) << shift
789
+				if b < 0x80 {
790
+					break
791
+				}
792
+			}
793
+			m.ServiceDisabled = bool(v != 0)
774 794
 		default:
775 795
 			iNdEx = preIndex
776 796
 			skippy, err := skipAgent(dAtA[iNdEx:])
... ...
@@ -1036,33 +1062,34 @@ var (
1036 1036
 func init() { proto.RegisterFile("agent.proto", fileDescriptorAgent) }
1037 1037
 
1038 1038
 var fileDescriptorAgent = []byte{
1039
-	// 437 bytes of a gzipped FileDescriptorProto
1040
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xc1, 0x6e, 0xd3, 0x30,
1041
-	0x18, 0xc7, 0x9b, 0x36, 0x6c, 0xcd, 0x97, 0xb6, 0x54, 0x16, 0x42, 0x51, 0x0e, 0x69, 0xa8, 0x84,
1042
-	0xd4, 0x03, 0xea, 0xa4, 0x71, 0xdc, 0x89, 0xb5, 0x1c, 0x72, 0x41, 0x96, 0xd7, 0x71, 0x0d, 0x69,
1043
-	0x63, 0x82, 0xb5, 0x10, 0x47, 0xb6, 0x37, 0xae, 0xdc, 0x40, 0x7b, 0x87, 0x9d, 0x78, 0x19, 0x4e,
1044
-	0x88, 0x23, 0xa7, 0x89, 0xe5, 0x09, 0x78, 0x04, 0x64, 0x27, 0x5e, 0x35, 0x69, 0x37, 0xfb, 0xf7,
1045
-	0xff, 0xd9, 0xfa, 0xbe, 0x3f, 0xf8, 0x59, 0x41, 0x2b, 0xb5, 0xac, 0x05, 0x57, 0x1c, 0x41, 0xc9,
1046
-	0xb6, 0x15, 0x55, 0x5f, 0xb8, 0xb8, 0x08, 0x9f, 0x15, 0xbc, 0xe0, 0x06, 0x1f, 0xe9, 0x53, 0x6b,
1047
-	0xcc, 0x7f, 0xf5, 0x61, 0xf2, 0xb6, 0xca, 0x6b, 0xce, 0x2a, 0x45, 0xe8, 0x8e, 0x8b, 0x1c, 0x21,
1048
-	0x70, 0xab, 0xec, 0x33, 0x0d, 0x9c, 0xd8, 0x59, 0x78, 0xc4, 0x9c, 0xd1, 0x0b, 0x18, 0x49, 0x2a,
1049
-	0xae, 0xd8, 0x8e, 0xa6, 0x26, 0xeb, 0x9b, 0xcc, 0xef, 0xd8, 0x3b, 0xad, 0xbc, 0x02, 0xb0, 0x0a,
1050
-	0xcb, 0x83, 0x81, 0x16, 0x4e, 0xc7, 0xcd, 0xed, 0xcc, 0x3b, 0x6b, 0x69, 0xb2, 0x26, 0x5e, 0x27,
1051
-	0x24, 0xb9, 0xb6, 0xaf, 0x98, 0x50, 0x97, 0x59, 0x99, 0xb2, 0x3a, 0x70, 0xf7, 0xf6, 0xfb, 0x96,
1052
-	0x26, 0x98, 0x78, 0x9d, 0x90, 0xd4, 0xe8, 0x08, 0x7c, 0xda, 0x0d, 0xa9, 0xf5, 0x27, 0x46, 0x9f,
1053
-	0x34, 0xb7, 0x33, 0xb0, 0xb3, 0x27, 0x98, 0x80, 0x55, 0x92, 0x1a, 0x9d, 0xc0, 0x98, 0x55, 0x85,
1054
-	0xa0, 0x52, 0xa6, 0x35, 0x17, 0x4a, 0x06, 0x07, 0xf1, 0x60, 0xe1, 0x1f, 0x3f, 0x5f, 0xee, 0x0b,
1055
-	0x59, 0x62, 0x2e, 0xd4, 0x8a, 0x57, 0x1f, 0x59, 0x41, 0x46, 0x9d, 0xac, 0x91, 0x44, 0x01, 0x1c,
1056
-	0x66, 0x25, 0xcb, 0x24, 0x95, 0xc1, 0x61, 0x3c, 0x58, 0x78, 0xc4, 0x5e, 0x75, 0x0d, 0x2a, 0x93,
1057
-	0x17, 0xa9, 0x8d, 0x87, 0x26, 0xf6, 0x35, 0x7b, 0xd3, 0xa2, 0xf9, 0xb7, 0x3e, 0xc0, 0xfe, 0xe7,
1058
-	0x47, 0xcb, 0x3c, 0x81, 0xa1, 0x29, 0x7f, 0xc7, 0x4b, 0x53, 0xe4, 0xe4, 0x78, 0xf6, 0xf8, 0x5c,
1059
-	0x4b, 0xdc, 0x69, 0xe4, 0xfe, 0x01, 0x9a, 0x81, 0xaf, 0x32, 0x51, 0x50, 0x65, 0x16, 0x33, 0x3d,
1060
-	0x8f, 0x09, 0xb4, 0x48, 0xbf, 0x44, 0x2f, 0x61, 0x52, 0x5f, 0x6e, 0x4b, 0x26, 0x3f, 0xd1, 0xbc,
1061
-	0x75, 0x5c, 0xe3, 0x8c, 0xef, 0xa9, 0xd6, 0xe6, 0x1f, 0x60, 0x68, 0x7f, 0x47, 0x01, 0x0c, 0x36,
1062
-	0x2b, 0x3c, 0xed, 0x85, 0x4f, 0xaf, 0x6f, 0x62, 0xdf, 0xe2, 0xcd, 0x0a, 0xeb, 0xe4, 0x7c, 0x8d,
1063
-	0xa7, 0xce, 0xc3, 0xe4, 0x7c, 0x8d, 0x51, 0x08, 0xee, 0xd9, 0x6a, 0x83, 0xa7, 0xfd, 0x70, 0x7a,
1064
-	0x7d, 0x13, 0x8f, 0x6c, 0xa4, 0x59, 0xe8, 0x7e, 0xff, 0x11, 0xf5, 0x4e, 0x83, 0x3f, 0x77, 0x51,
1065
-	0xef, 0xdf, 0x5d, 0xe4, 0x7c, 0x6d, 0x22, 0xe7, 0x67, 0x13, 0x39, 0xbf, 0x9b, 0xc8, 0xf9, 0xdb,
1066
-	0x44, 0xce, 0xf6, 0xc0, 0x6c, 0xf3, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xce, 0x12, 0x15,
1067
-	0x67, 0xac, 0x02, 0x00, 0x00,
1039
+	// 459 bytes of a gzipped FileDescriptorProto
1040
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0x31, 0x6f, 0xd3, 0x4c,
1041
+	0x18, 0xc7, 0xe3, 0xc4, 0x6f, 0x1b, 0x3f, 0x4e, 0x52, 0xeb, 0xf4, 0x0a, 0x59, 0x1e, 0x1c, 0x13,
1042
+	0x09, 0x29, 0x48, 0x28, 0x95, 0xca, 0xd8, 0x89, 0x26, 0x0c, 0x5e, 0x90, 0x75, 0x4d, 0x59, 0x83,
1043
+	0x13, 0x1f, 0xe6, 0x54, 0xe3, 0xb3, 0xee, 0xae, 0x65, 0x65, 0x03, 0xf5, 0x3b, 0x74, 0xe2, 0xcb,
1044
+	0x30, 0x32, 0x32, 0x55, 0xd4, 0x9f, 0x80, 0x95, 0x0d, 0xdd, 0xf9, 0xae, 0x11, 0x52, 0xb7, 0xf3,
1045
+	0xef, 0xff, 0x3b, 0xeb, 0xb9, 0xff, 0x03, 0x7e, 0x5e, 0x92, 0x5a, 0x2e, 0x1a, 0xce, 0x24, 0x43,
1046
+	0x50, 0xd1, 0x6d, 0x4d, 0xe4, 0x27, 0xc6, 0x2f, 0xa3, 0xff, 0x4b, 0x56, 0x32, 0x8d, 0x8f, 0xd5,
1047
+	0xa9, 0x33, 0x66, 0x7f, 0xfa, 0x30, 0x79, 0x5d, 0x17, 0x0d, 0xa3, 0xb5, 0xc4, 0x64, 0xc7, 0x78,
1048
+	0x81, 0x10, 0xb8, 0x75, 0xfe, 0x91, 0x84, 0x4e, 0xe2, 0xcc, 0x3d, 0xac, 0xcf, 0xe8, 0x29, 0x8c,
1049
+	0x04, 0xe1, 0xd7, 0x74, 0x47, 0x36, 0x3a, 0xeb, 0xeb, 0xcc, 0x37, 0xec, 0x8d, 0x52, 0x5e, 0x00,
1050
+	0x58, 0x85, 0x16, 0xe1, 0x40, 0x09, 0x67, 0xe3, 0xf6, 0x6e, 0xea, 0x9d, 0x77, 0x34, 0x5d, 0x61,
1051
+	0xcf, 0x08, 0x69, 0xa1, 0xec, 0x6b, 0xca, 0xe5, 0x55, 0x5e, 0x6d, 0x68, 0x13, 0xba, 0x7b, 0xfb,
1052
+	0x6d, 0x47, 0xd3, 0x0c, 0x7b, 0x46, 0x48, 0x1b, 0x74, 0x0c, 0x3e, 0x31, 0x43, 0x2a, 0xfd, 0x3f,
1053
+	0xad, 0x4f, 0xda, 0xbb, 0x29, 0xd8, 0xd9, 0xd3, 0x0c, 0x83, 0x55, 0xd2, 0x06, 0x9d, 0xc2, 0x98,
1054
+	0xd6, 0x25, 0x27, 0x42, 0x6c, 0x1a, 0xc6, 0xa5, 0x08, 0x0f, 0x92, 0xc1, 0xdc, 0x3f, 0x79, 0xb2,
1055
+	0xd8, 0x17, 0xb2, 0xc8, 0x18, 0x97, 0x4b, 0x56, 0xbf, 0xa7, 0x25, 0x1e, 0x19, 0x59, 0x21, 0x81,
1056
+	0x42, 0x38, 0xcc, 0x2b, 0x9a, 0x0b, 0x22, 0xc2, 0xc3, 0x64, 0x30, 0xf7, 0xb0, 0xfd, 0x54, 0x35,
1057
+	0xc8, 0x5c, 0x5c, 0x6e, 0x6c, 0x3c, 0xd4, 0xb1, 0xaf, 0xd8, 0x2b, 0xa3, 0x3c, 0x87, 0xc0, 0xd6,
1058
+	0x50, 0x50, 0x91, 0x6f, 0x2b, 0x52, 0x84, 0x5e, 0xe2, 0xcc, 0x87, 0xf8, 0xc8, 0xf0, 0x95, 0xc1,
1059
+	0xb3, 0x2f, 0x7d, 0x80, 0xfd, 0x10, 0x8f, 0xf6, 0x7e, 0x0a, 0x43, 0xbd, 0xa7, 0x1d, 0xab, 0x74,
1060
+	0xe7, 0x93, 0x93, 0xe9, 0xe3, 0x4f, 0x58, 0x64, 0x46, 0xc3, 0x0f, 0x17, 0xd0, 0x14, 0x7c, 0x99,
1061
+	0xf3, 0x92, 0x48, 0xdd, 0x81, 0x5e, 0xc9, 0x18, 0x43, 0x87, 0xd4, 0x4d, 0xf4, 0x0c, 0x26, 0xcd,
1062
+	0xd5, 0xb6, 0xa2, 0xe2, 0x03, 0x29, 0x3a, 0xc7, 0xd5, 0xce, 0xf8, 0x81, 0x2a, 0x6d, 0xf6, 0x0e,
1063
+	0x86, 0xf6, 0xef, 0x28, 0x84, 0xc1, 0x7a, 0x99, 0x05, 0xbd, 0xe8, 0xe8, 0xe6, 0x36, 0xf1, 0x2d,
1064
+	0x5e, 0x2f, 0x33, 0x95, 0x5c, 0xac, 0xb2, 0xc0, 0xf9, 0x37, 0xb9, 0x58, 0x65, 0x28, 0x02, 0xf7,
1065
+	0x7c, 0xb9, 0xce, 0x82, 0x7e, 0x14, 0xdc, 0xdc, 0x26, 0x23, 0x1b, 0x29, 0x16, 0xb9, 0x5f, 0xbf,
1066
+	0xc5, 0xbd, 0xb3, 0xf0, 0xe7, 0x7d, 0xdc, 0xfb, 0x7d, 0x1f, 0x3b, 0x9f, 0xdb, 0xd8, 0xf9, 0xde,
1067
+	0xc6, 0xce, 0x8f, 0x36, 0x76, 0x7e, 0xb5, 0xb1, 0xb3, 0x3d, 0xd0, 0xaf, 0x79, 0xf9, 0x37, 0x00,
1068
+	0x00, 0xff, 0xff, 0x55, 0x29, 0x75, 0x5c, 0xd7, 0x02, 0x00, 0x00,
1068 1069
 }
... ...
@@ -37,6 +37,9 @@ message EndpointRecord {
37 37
 
38 38
 	// List of aliases task specific aliases
39 39
 	repeated string task_aliases = 8;
40
+
41
+	// Whether this enpoint's service has been disabled
42
+	bool service_disabled = 9;
40 43
 }
41 44
 
42 45
 // PortConfig specifies an exposed port which can be
... ...
@@ -620,7 +620,7 @@ func (ep *endpoint) rename(name string) error {
620 620
 	}
621 621
 
622 622
 	if c.isAgent() {
623
-		if err = ep.deleteServiceInfoFromCluster(sb, "rename"); err != nil {
623
+		if err = ep.deleteServiceInfoFromCluster(sb, true, "rename"); err != nil {
624 624
 			return types.InternalErrorf("Could not delete service state for endpoint %s from cluster on rename: %v", ep.Name(), err)
625 625
 		}
626 626
 	} else {
... ...
@@ -644,7 +644,7 @@ func (ep *endpoint) rename(name string) error {
644 644
 		}
645 645
 		defer func() {
646 646
 			if err != nil {
647
-				ep.deleteServiceInfoFromCluster(sb, "rename")
647
+				ep.deleteServiceInfoFromCluster(sb, true, "rename")
648 648
 				ep.name = oldName
649 649
 				ep.anonymous = oldAnonymous
650 650
 				ep.addServiceInfoToCluster(sb)
... ...
@@ -755,8 +755,14 @@ func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption)
755 755
 		}
756 756
 	}
757 757
 
758
+	if ep.svcID != "" {
759
+		if err := ep.deleteServiceInfoFromCluster(sb, true, "sbLeave"); err != nil {
760
+			logrus.Warnf("Failed to clean up service info on container %s disconnect: %v", ep.name, err)
761
+		}
762
+	}
763
+
758 764
 	if err := sb.clearNetworkResources(ep); err != nil {
759
-		logrus.Warnf("Could not cleanup network resources on container %s disconnect: %v", ep.name, err)
765
+		logrus.Warnf("Failed to clean up network resources on container %s disconnect: %v", ep.name, err)
760 766
 	}
761 767
 
762 768
 	// Update the store about the sandbox detach only after we
... ...
@@ -769,7 +775,7 @@ func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption)
769 769
 	}
770 770
 
771 771
 	if e := ep.deleteDriverInfoFromCluster(); e != nil {
772
-		logrus.Errorf("Could not delete endpoint state for endpoint %s from cluster: %v", ep.Name(), e)
772
+		logrus.Errorf("Failed to delete endpoint state for endpoint %s from cluster: %v", ep.Name(), e)
773 773
 	}
774 774
 
775 775
 	sb.deleteHostsEntries(n.getSvcRecords(ep))
... ...
@@ -703,7 +703,7 @@ func (sb *sandbox) DisableService() (err error) {
703 703
 	}()
704 704
 	for _, ep := range sb.getConnectedEndpoints() {
705 705
 		if ep.isServiceEnabled() {
706
-			if err := ep.deleteServiceInfoFromCluster(sb, "DisableService"); err != nil {
706
+			if err := ep.deleteServiceInfoFromCluster(sb, false, "DisableService"); err != nil {
707 707
 				failedEps = append(failedEps, ep.Name())
708 708
 				logrus.Warnf("failed update state for endpoint %s into cluster: %v", ep.Name(), err)
709 709
 			}
... ...
@@ -79,13 +79,18 @@ func (s *service) printIPToEndpoint(ip string) (string, bool) {
79 79
 	return s.ipToEndpoint.String(ip)
80 80
 }
81 81
 
82
+type lbBackend struct {
83
+	ip       net.IP
84
+	disabled bool
85
+}
86
+
82 87
 type loadBalancer struct {
83 88
 	vip    net.IP
84 89
 	fwMark uint32
85 90
 
86 91
 	// Map of backend IPs backing this loadbalancer on this
87 92
 	// network. It is keyed with endpoint ID.
88
-	backEnds map[string]net.IP
93
+	backEnds map[string]*lbBackend
89 94
 
90 95
 	// Back pointer to service to which the loadbalancer belongs.
91 96
 	service *service
... ...
@@ -198,23 +198,8 @@ func (c *controller) cleanupServiceBindings(cleanupNID string) {
198 198
 			if cleanupNID != "" && nid != cleanupNID {
199 199
 				continue
200 200
 			}
201
-
202
-			for eid, ip := range lb.backEnds {
203
-				epID := eid
204
-				epIP := ip
205
-				service := s
206
-				loadBalancer := lb
207
-				networkID := nid
208
-				cleanupFuncs = append(cleanupFuncs, func() {
209
-					// ContainerName and taskAliases are not available here, this is still fine because the Service discovery
210
-					// cleanup already happened before. The only thing that rmServiceBinding is still doing here a part from the Load
211
-					// Balancer bookeeping, is to keep consistent the mapping of endpoint to IP.
212
-					if err := c.rmServiceBinding(service.name, service.id, networkID, epID, "", loadBalancer.vip,
213
-						service.ingressPorts, service.aliases, []string{}, epIP, "cleanupServiceBindings", false); err != nil {
214
-						logrus.Errorf("Failed to remove service bindings for service %s network %s endpoint %s while cleanup: %v",
215
-							service.id, networkID, epID, err)
216
-					}
217
-				})
201
+			for eid, be := range lb.backEnds {
202
+				cleanupFuncs = append(cleanupFuncs, makeServiceCleanupFunc(c, s, nid, eid, lb.vip, be.ip))
218 203
 			}
219 204
 		}
220 205
 		s.Unlock()
... ...
@@ -226,6 +211,17 @@ func (c *controller) cleanupServiceBindings(cleanupNID string) {
226 226
 
227 227
 }
228 228
 
229
+func makeServiceCleanupFunc(c *controller, s *service, nID, eID string, vip net.IP, ip net.IP) func() {
230
+	// ContainerName and taskAliases are not available here, this is still fine because the Service discovery
231
+	// cleanup already happened before. The only thing that rmServiceBinding is still doing here a part from the Load
232
+	// Balancer bookeeping, is to keep consistent the mapping of endpoint to IP.
233
+	return func() {
234
+		if err := c.rmServiceBinding(s.name, s.id, nID, eID, "", vip, s.ingressPorts, s.aliases, []string{}, ip, "cleanupServiceBindings", false, true); err != nil {
235
+			logrus.Errorf("Failed to remove service bindings for service %s network %s endpoint %s while cleanup: %v", s.id, nID, eID, err)
236
+		}
237
+	}
238
+}
239
+
229 240
 func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases, taskAliases []string, ip net.IP, method string) error {
230 241
 	var addService bool
231 242
 
... ...
@@ -271,7 +267,7 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
271 271
 		lb = &loadBalancer{
272 272
 			vip:      vip,
273 273
 			fwMark:   fwMarkCtr,
274
-			backEnds: make(map[string]net.IP),
274
+			backEnds: make(map[string]*lbBackend),
275 275
 			service:  s,
276 276
 		}
277 277
 
... ...
@@ -282,7 +278,7 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
282 282
 		addService = true
283 283
 	}
284 284
 
285
-	lb.backEnds[eID] = ip
285
+	lb.backEnds[eID] = &lbBackend{ip, false}
286 286
 
287 287
 	ok, entries := s.assignIPToEndpoint(ip.String(), eID)
288 288
 	if !ok || entries > 1 {
... ...
@@ -307,7 +303,7 @@ func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName s
307 307
 	return nil
308 308
 }
309 309
 
310
-func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases []string, taskAliases []string, ip net.IP, method string, deleteSvcRecords bool) error {
310
+func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases []string, taskAliases []string, ip net.IP, method string, deleteSvcRecords bool, fullRemove bool) error {
311 311
 
312 312
 	var rmService bool
313 313
 
... ...
@@ -338,13 +334,19 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st
338 338
 		return nil
339 339
 	}
340 340
 
341
-	_, ok = lb.backEnds[eID]
341
+	be, ok := lb.backEnds[eID]
342 342
 	if !ok {
343
-		logrus.Warnf("rmServiceBinding %s %s %s aborted lb.backEnds[eid] !ok", method, svcName, eID)
343
+		logrus.Warnf("rmServiceBinding %s %s %s aborted lb.backEnds[eid] && lb.disabled[eid] !ok", method, svcName, eID)
344 344
 		return nil
345 345
 	}
346 346
 
347
-	delete(lb.backEnds, eID)
347
+	if fullRemove {
348
+		// delete regardless
349
+		delete(lb.backEnds, eID)
350
+	} else {
351
+		be.disabled = true
352
+	}
353
+
348 354
 	if len(lb.backEnds) == 0 {
349 355
 		// All the backends for this service have been
350 356
 		// removed. Time to remove the load balancer and also
... ...
@@ -367,7 +369,7 @@ func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName st
367 367
 	// Remove loadbalancer service(if needed) and backend in all
368 368
 	// sandboxes in the network only if the vip is valid.
369 369
 	if len(vip) != 0 && entries == 0 {
370
-		n.(*network).rmLBBackend(ip, vip, lb, ingressPorts, rmService)
370
+		n.(*network).rmLBBackend(ip, vip, lb, ingressPorts, rmService, fullRemove)
371 371
 	}
372 372
 
373 373
 	// Delete the name resolutions
... ...
@@ -103,8 +103,10 @@ func (sb *sandbox) populateLoadbalancers(ep *endpoint) {
103 103
 		}
104 104
 
105 105
 		lb.service.Lock()
106
-		for _, ip := range lb.backEnds {
107
-			sb.addLBBackend(ip, lb.vip, lb.fwMark, lb.service.ingressPorts, eIP, gwIP, n.ingress)
106
+		for _, be := range lb.backEnds {
107
+			if !be.disabled {
108
+				sb.addLBBackend(be.ip, lb.vip, lb.fwMark, lb.service.ingressPorts, eIP, gwIP, n.ingress)
109
+			}
108 110
 		}
109 111
 		lb.service.Unlock()
110 112
 	}
... ...
@@ -135,7 +137,7 @@ func (n *network) addLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []
135 135
 // Remove loadbalancer backend from all sandboxes which has a
136 136
 // connection to this network. If needed remove the service entry as
137 137
 // well, as specified by the rmService bool.
138
-func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig, rmService bool) {
138
+func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig, rmService bool, fullRemove bool) {
139 139
 	n.WalkEndpoints(func(e Endpoint) bool {
140 140
 		ep := e.(*endpoint)
141 141
 		if sb, ok := ep.getSandbox(); ok {
... ...
@@ -148,7 +150,7 @@ func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*
148 148
 				gwIP = ep.Iface().Address().IP
149 149
 			}
150 150
 
151
-			sb.rmLBBackend(ip, vip, lb.fwMark, ingressPorts, ep.Iface().Address(), gwIP, rmService, n.ingress)
151
+			sb.rmLBBackend(ip, vip, lb.fwMark, ingressPorts, ep.Iface().Address(), gwIP, rmService, fullRemove, n.ingress)
152 152
 		}
153 153
 
154 154
 		return false
... ...
@@ -215,7 +217,7 @@ func (sb *sandbox) addLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*P
215 215
 }
216 216
 
217 217
 // Remove loadbalancer backend from one connected sandbox.
218
-func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, gwIP net.IP, rmService bool, isIngressNetwork bool) {
218
+func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*PortConfig, eIP *net.IPNet, gwIP net.IP, rmService bool, fullRemove bool, isIngressNetwork bool) {
219 219
 	if sb.osSbox == nil {
220 220
 		return
221 221
 	}
... ...
@@ -242,8 +244,15 @@ func (sb *sandbox) rmLBBackend(ip, vip net.IP, fwMark uint32, ingressPorts []*Po
242 242
 		Weight:        1,
243 243
 	}
244 244
 
245
-	if err := i.DelDestination(s, d); err != nil && err != syscall.ENOENT {
246
-		logrus.Errorf("Failed to delete real server %s for vip %s fwmark %d in sbox %s (%s): %v", ip, vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err)
245
+	if fullRemove {
246
+		if err := i.DelDestination(s, d); err != nil && err != syscall.ENOENT {
247
+			logrus.Errorf("Failed to delete real server %s for vip %s fwmark %d in sbox %s (%s): %v", ip, vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err)
248
+		}
249
+	} else {
250
+		d.Weight = 0
251
+		if err := i.UpdateDestination(s, d); err != nil && err != syscall.ENOENT {
252
+			logrus.Errorf("Failed to set LB weight of real server %s to 0 for vip %s fwmark %d in sbox %s (%s): %v", ip, vip, fwMark, sb.ID()[0:7], sb.ContainerID()[0:7], err)
253
+		}
247 254
 	}
248 255
 
249 256
 	if rmService {
... ...
@@ -44,7 +44,10 @@ func (n *network) addLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []
44 44
 
45 45
 		var endpoints []hcsshim.HNSEndpoint
46 46
 
47
-		for eid := range lb.backEnds {
47
+		for eid, be := range lb.backEnds {
48
+			if be.disabled {
49
+				continue
50
+			}
48 51
 			//Call HNS to get back ID (GUID) corresponding to the endpoint.
49 52
 			hnsEndpoint, err := hcsshim.GetHNSEndpointByName(eid)
50 53
 			if err != nil {
... ...
@@ -114,9 +117,9 @@ func (n *network) addLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []
114 114
 	}
115 115
 }
116 116
 
117
-func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig, rmService bool) {
117
+func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*PortConfig, rmService bool, fullRemove bool) {
118 118
 	if system.GetOSVersion().Build > 16236 {
119
-		if len(lb.backEnds) > 0 {
119
+		if numEnabledBackends(lb) > 0 {
120 120
 			//Reprogram HNS (actually VFP) with the existing backends.
121 121
 			n.addLBBackend(ip, vip, lb, ingressPorts)
122 122
 		} else {
... ...
@@ -143,6 +146,16 @@ func (n *network) rmLBBackend(ip, vip net.IP, lb *loadBalancer, ingressPorts []*
143 143
 	}
144 144
 }
145 145
 
146
+func numEnabledBackends(lb *loadBalancer) int {
147
+	nEnabled := 0
148
+	for _, be := range lb.backEnds {
149
+		if !be.disabled {
150
+			nEnabled++
151
+		}
152
+	}
153
+	return nEnabled
154
+}
155
+
146 156
 func (sb *sandbox) populateLoadbalancers(ep *endpoint) {
147 157
 }
148 158