func TestLoadBalanceWorksWithServiceRemoval(t *testing.T) { loadBalancer := NewLoadBalancerRR() fooService := types.NewNamespacedNameOrDie("testnamespace", "foo") barService := types.NewNamespacedNameOrDie("testnamespace", "bar") endpoint, err := loadBalancer.NextEndpoint(fooService, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } endpoints := make([]api.Endpoints, 2) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: fooService.Name, Namespace: fooService.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 1}, {IP: "endpoint", Port: 2}, {IP: "endpoint", Port: 3}, }, } endpoints[1] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: barService.Name, Namespace: barService.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 4}, {IP: "endpoint", Port: 5}, }, } loadBalancer.OnUpdate(endpoints) shuffledFooEndpoints := loadBalancer.services[fooService].endpoints expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], nil) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[1], nil) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[2], nil) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], nil) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[1], nil) shuffledBarEndpoints := loadBalancer.services[barService].endpoints expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[1], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[1], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], nil) // Then update the configuration by removing foo loadBalancer.OnUpdate(endpoints[1:]) endpoint, err = loadBalancer.NextEndpoint(fooService, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } // but bar is still there, and we continue RR from where we left off. expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[1], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[1], nil) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], nil) }
func TestStickyLoadBalanaceWorksWithMultipleEndpointsStickyNone(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} client3 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 3), Port: 0} loadBalancer := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } loadBalancer.NewService(service, api.AffinityTypeNone, 0) endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 1}, {IP: "endpoint", Port: 2}, {IP: "endpoint", Port: 3}, }, } loadBalancer.OnUpdate(endpoints) 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) }
func TestUDPProxyUpdatePort(t *testing.T) { lb := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "echo") lb.OnUpdate([]api.Endpoints{ { ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{{IP: "127.0.0.1", Port: udpServerPort}}, }, }) p := CreateProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1")) waitForNumProxyLoops(t, p, 0) svcInfo, err := p.addServiceOnPort(service, "UDP", 0, time.Second) if err != nil { t.Fatalf("error adding new service: %#v", err) } waitForNumProxyLoops(t, p, 1) p.OnUpdate([]api.Service{ {ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Spec: api.ServiceSpec{Port: 99, Protocol: "UDP"}, Status: api.ServiceStatus{}}, }) // Wait for the socket to actually get free. if err := waitForClosedPortUDP(p, svcInfo.proxyPort); err != nil { t.Fatalf(err.Error()) } svcInfo, exists := p.getServiceInfo(service) if !exists { t.Fatalf("can't find serviceInfo") } testEchoUDP(t, "127.0.0.1", svcInfo.proxyPort) waitForNumProxyLoops(t, p, 1) }
func TestUDPProxyUpdateDelete(t *testing.T) { lb := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "echo") lb.OnUpdate([]api.Endpoints{ { ObjectMeta: api.ObjectMeta{Namespace: service.Namespace, Name: service.Name}, Endpoints: []api.Endpoint{{IP: "127.0.0.1", Port: udpServerPort}}, }, }) p := CreateProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1")) waitForNumProxyLoops(t, p, 0) svcInfo, err := p.addServiceOnPort(service, "UDP", 0, time.Second) if err != nil { t.Fatalf("error adding new service: %#v", err) } conn, err := net.Dial("udp", joinHostPort("", svcInfo.proxyPort)) if err != nil { t.Fatalf("error connecting to proxy: %v", err) } conn.Close() waitForNumProxyLoops(t, p, 1) p.OnUpdate([]api.Service{}) if err := waitForClosedPortUDP(p, svcInfo.proxyPort); err != nil { t.Fatalf(err.Error()) } waitForNumProxyLoops(t, p, 0) }
func TestStickyLoadBalanceWorksWithMultipleEndpointsAndUpdates(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} client3 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 3), Port: 0} loadBalancer := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } loadBalancer.NewService(service, api.AffinityTypeClientIP, 0) endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 1}, {IP: "endpoint", Port: 2}, {IP: "endpoint", Port: 3}, }, } loadBalancer.OnUpdate(endpoints) 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[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client2) // Then update the configuration with one fewer endpoints, make sure // we start in the beginning again endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 4}, {IP: "endpoint", Port: 5}, }, } loadBalancer.OnUpdate(endpoints) shuffledEndpoints = loadBalancer.services[service].endpoints expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client2) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], client1) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client2) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], client2) // Clear endpoints endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{}} loadBalancer.OnUpdate(endpoints) endpoint, err = loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } }
func TestLoadBalanceFailsWithNoEndpoints(t *testing.T) { loadBalancer := NewLoadBalancerRR() var endpoints []api.Endpoints loadBalancer.OnUpdate(endpoints) service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil { t.Errorf("Didn't fail with non-existent service") } if len(endpoint) != 0 { t.Errorf("Got an endpoint") } }
func TestLoadBalanceWorksWithMultipleEndpointsAndUpdates(t *testing.T) { loadBalancer := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 1}, {IP: "endpoint", Port: 2}, {IP: "endpoint", Port: 3}, }, } loadBalancer.OnUpdate(endpoints) shuffledEndpoints := loadBalancer.services[service].endpoints expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[2], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], nil) // Then update the configuration with one fewer endpoints, make sure // we start in the beginning again endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 8}, {IP: "endpoint", Port: 9}, }, } loadBalancer.OnUpdate(endpoints) shuffledEndpoints = loadBalancer.services[service].endpoints expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], nil) // Clear endpoints endpoints[0] = api.Endpoints{ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{}} loadBalancer.OnUpdate(endpoints) endpoint, err = loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } }
func TestLoadBalanceWorksWithSingleEndpoint(t *testing.T) { loadBalancer := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{{IP: "endpoint1", Port: 40}}, } loadBalancer.OnUpdate(endpoints) expectEndpoint(t, loadBalancer, service, "endpoint1:40", nil) expectEndpoint(t, loadBalancer, service, "endpoint1:40", nil) expectEndpoint(t, loadBalancer, service, "endpoint1:40", nil) expectEndpoint(t, loadBalancer, service, "endpoint1:40", nil) }
func TestUDPProxy(t *testing.T) { lb := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "echo") lb.OnUpdate([]api.Endpoints{ { ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{{IP: "127.0.0.1", Port: udpServerPort}}, }, }) p := CreateProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1")) waitForNumProxyLoops(t, p, 0) svcInfo, err := p.addServiceOnPort(service, "UDP", 0, time.Second) if err != nil { t.Fatalf("error adding new service: %#v", err) } testEchoUDP(t, "127.0.0.1", svcInfo.proxyPort) waitForNumProxyLoops(t, p, 1) }
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} loadBalancer := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } loadBalancer.NewService(service, api.AffinityTypeClientIP, 0) endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{{IP: "endpoint", Port: 1}}, } loadBalancer.OnUpdate(endpoints) 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 TestTCPProxyUpdateDeleteUpdate(t *testing.T) { lb := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "echo") lb.OnUpdate([]api.Endpoints{ { ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{{IP: "127.0.0.1", Port: tcpServerPort}}, }, }) p := CreateProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1")) waitForNumProxyLoops(t, p, 0) svcInfo, err := p.addServiceOnPort(service, "TCP", 0, time.Second) if err != nil { t.Fatalf("error adding new service: %#v", err) } conn, err := net.Dial("tcp", joinHostPort("", svcInfo.proxyPort)) if err != nil { t.Fatalf("error connecting to proxy: %v", err) } conn.Close() waitForNumProxyLoops(t, p, 1) p.OnUpdate([]api.Service{}) if err := waitForClosedPortTCP(p, svcInfo.proxyPort); err != nil { t.Fatalf(err.Error()) } waitForNumProxyLoops(t, p, 0) p.OnUpdate([]api.Service{ {ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Spec: api.ServiceSpec{Port: svcInfo.proxyPort, Protocol: "TCP"}, Status: api.ServiceStatus{}}, }) svcInfo, exists := p.getServiceInfo(service) if !exists { t.Fatalf("can't find serviceInfo for %s", service) } testEchoTCP(t, "127.0.0.1", svcInfo.proxyPort) waitForNumProxyLoops(t, p, 1) }
func TestLoadBalanceWorksWithMultipleEndpoints(t *testing.T) { loadBalancer := NewLoadBalancerRR() service := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(service, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } endpoints := make([]api.Endpoints, 1) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 1}, {IP: "endpoint", Port: 2}, {IP: "endpoint", Port: 3}, }, } loadBalancer.OnUpdate(endpoints) shuffledEndpoints := loadBalancer.services[service].endpoints expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[1], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[2], nil) expectEndpoint(t, loadBalancer, service, shuffledEndpoints[0], nil) }
func TestStickyLoadBalanceWorksWithServiceRemoval(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} client3 := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 3), Port: 0} loadBalancer := NewLoadBalancerRR() fooService := types.NewNamespacedNameOrDie("testnamespace", "foo") endpoint, err := loadBalancer.NextEndpoint(fooService, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } loadBalancer.NewService(fooService, api.AffinityTypeClientIP, 0) endpoints := make([]api.Endpoints, 2) endpoints[0] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: fooService.Name, Namespace: fooService.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 1}, {IP: "endpoint", Port: 2}, {IP: "endpoint", Port: 3}, }, } barService := types.NewNamespacedNameOrDie("testnamespace", "bar") loadBalancer.NewService(barService, api.AffinityTypeClientIP, 0) endpoints[1] = api.Endpoints{ ObjectMeta: api.ObjectMeta{Name: barService.Name, Namespace: barService.Namespace}, Endpoints: []api.Endpoint{ {IP: "endpoint", Port: 5}, {IP: "endpoint", Port: 5}, }, } loadBalancer.OnUpdate(endpoints) shuffledFooEndpoints := loadBalancer.services[fooService].endpoints expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], client1) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[1], client2) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[2], client3) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[2], client3) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], client1) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[1], client2) shuffledBarEndpoints := loadBalancer.services[barService].endpoints expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], client1) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[1], client2) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], client1) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[1], client2) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], client1) expectEndpoint(t, loadBalancer, fooService, shuffledFooEndpoints[0], client1) // Then update the configuration by removing foo loadBalancer.OnUpdate(endpoints[1:]) endpoint, err = loadBalancer.NextEndpoint(fooService, nil) if err == nil || len(endpoint) != 0 { t.Errorf("Didn't fail with non-existent service") } // but bar is still there, and we continue RR from where we left off. shuffledBarEndpoints = loadBalancer.services[barService].endpoints expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], client1) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[1], client2) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], client1) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[1], client2) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], client1) expectEndpoint(t, loadBalancer, barService, shuffledBarEndpoints[0], client1) }