Change:
!topic/google-containers/pjx13NWEFcg The bug seems to be in kube-proxy.

Discussion:
if _, exists := lb.services[svcPort]; !exists { lb.services[svcPort] = &balancerState{affinity: *newAffinityPolicy(affinityType, ttlMinutes)} glog.V(4).Infof("LoadBalancerRR service %q did not exist, created", svcPort) <ins> } else if affinityType != "" { lb.services[svcPort].affinity.affinityType = affinityType </ins> } return lb.services[svcPort] }

lb.updateAffinityMap(svcPort, newEndpoints) // OnUpdate can be called without NewService being called externally. // To be safe we will call it here.  A new service will only be created <del> // if one does not already exist. state = lb.newServiceInternal(svcPort, api.ServiceAffinityNone, 0) </del> <ins> // if one does not already exist.  The affinity will be updated // later, once NewService is called. state = lb.newServiceInternal(svcPort, api.ServiceAffinity(""), 0) </ins> state.endpoints = slice.ShuffleStrings(newEndpoints) // Reset the round-robin index.

expectEndpoint(t, loadBalancer, barServiceP, shuffledBarEndpoints[2], nil) } <del> func TestStickyLoadBalanceWorksWithSingleEndpoint(t *testing.T) { client1 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} client2 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 2), Port: 0} </del> <ins> func TestStickyLoadBalanceWorksWithNewServiceCalledFirst(t *testing.T) { </ins> loadBalancer := NewLoadBalancerRR() service := ServicePortName{types.NamespacedName{"testnamespace", "foo"}, ""} endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } <ins> // Call NewService() before OnUpdate() </ins> loadBalancer.NewService(service, api.ServiceAffinityClientIP, 0) endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, <del> Subsets:    []api.EndpointSubset{{Addresses: []api.EndpointAddress{{IP: "endpoint"}}, Ports: []api.EndpointPort{{Port: 1}}}}, </del> <ins> Subsets: []api.EndpointSubset{ {Addresses: []api.EndpointAddress{{IP: "endpoint1"}}, Ports: []api.EndpointPort{{Port: 1}}}, {Addresses: []api.EndpointAddress{{IP: "endpoint2"}}, Ports: []api.EndpointPort{{Port: 2}}}, {Addresses: []api.EndpointAddress{{IP: "endpoint3"}}, Ports: []api.EndpointPort{{Port: 3}}}, }, </ins> } loadBalancer.OnUpdate(endpoints) <del> expectEndpoint(t, loadBalancer, service, "endpoint:1", client1) expectEndpoint(t, loadBalancer, service, "endpoint:1", client1) expectEndpoint(t, loadBalancer, service, "endpoint:1", client2) expectEndpoint(t, loadBalancer, service, "endpoint:1", client2) } func TestStickyLoadBalanaceWorksWithMultipleEndpoints(t *testing.T) { </del> client1 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} client2 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 2), Port: 0} client3 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 3), Port: 0} <ins> ep1, err := loadBalancer.NextEndpoint(service, client1) if err != nil { t.Errorf("Didn't find a service for %s: %v", service, err) } expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep1, client1) ep2, err := loadBalancer.NextEndpoint(service, client2) if err != nil { t.Errorf("Didn't find a service for %s: %v", service, err) } expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep2, client2) ep3, err := loadBalancer.NextEndpoint(service, client3) if err != nil { t.Errorf("Didn't find a service for %s: %v", service, err) } expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep3, client3) } func TestStickyLoadBalanceWorksWithNewServiceCalledSecond(t *testing.T) { </ins> loadBalancer := NewLoadBalancerRR() service := ServicePortName{types.NamespacedName{"testnamespace", "foo"}, ""} endpoint, err := loadBalancer.NextEndpoint(service, nil)

t.Errorf("Didn't fail with non-existent service") } <del> loadBalancer.NewService(service, api.ServiceAffinityClientIP, 0) </del> <ins> // Call OnUpdate() before NewService() </ins> endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Subsets: []api.EndpointSubset{ <del> { Addresses: []api.EndpointAddress{{IP: "endpoint"}}, Ports:     []api.EndpointPort{{Port: 1}, {Port: 2}, {Port: 3}}, }, </del> <ins> {Addresses: []api.EndpointAddress{{IP: "endpoint1"}}, Ports: []api.EndpointPort{{Port: 1}}}, {Addresses: []api.EndpointAddress{{IP: "endpoint2"}}, Ports: []api.EndpointPort{{Port: 2}}}, </ins> }, } loadBalancer.OnUpdate(endpoints) <del> shuffledEndpoints := loadBalancer.services[service].endpoints expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client2) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client2) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[2], client3) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[2], client3) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) } </del> <ins> loadBalancer.NewService(service, api.ServiceAffinityClientIP, 0) </ins> <del> func TestStickyLoadBalanaceWorksWithMultipleEndpointsStickyNone(t *testing.T) { </del> client1 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} client2 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 2), Port: 0} client3 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 3), Port: 0} <del> loadBalancer := NewLoadBalancerRR() service := ServicePortName{types.NamespacedName{"testnamespace", "foo"}, ""} endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") </del> <ins> ep1, err := loadBalancer.NextEndpoint(service, client1) if err != nil { t.Errorf("Didn't find a service for %s: %v", service, err) </ins> } <ins> expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep1, client1) </ins> <del> loadBalancer.NewService(service, api.ServiceAffinityNone, 0) endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Subsets: []api.EndpointSubset{ { Addresses: []api.EndpointAddress{{IP: "endpoint"}}, Ports:     []api.EndpointPort{{Port: 1}, {Port: 2}, {Port: 3}}, }, }, </del> <ins> ep2, err := loadBalancer.NextEndpoint(service, client2) if err != nil { t.Errorf("Didn't find a service for %s: %v", service, err) </ins> } <del> loadBalancer.OnUpdate(endpoints) </del> <ins> expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep2, client2) </ins> <del> shuffledEndpoints := loadBalancer.services[service].endpoints expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[2], client2) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client2) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client3) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[2], client3) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client1) </del> <ins> ep3, err := loadBalancer.NextEndpoint(service, client3) if err != nil { t.Errorf("Didn't find a service for %s: %v", service, err) } expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep3, client3) expectEndpoint(t, loadBalancer, service, ep1, client1) expectEndpoint(t, loadBalancer, service, ep2, client2) expectEndpoint(t, loadBalancer, service, ep3, client3) </ins> } func TestStickyLoadBalanaceWorksWithMultipleEndpointsRemoveOne(t *testing.T) {