Change:
Depends on UDP clients have a timeout (so we don't leak connections forever), but the test does not cover this. To do this, we probably need to track active connections, and plumb that all the way down to the goroutines spawned by the ProxyLoop routines. This is probably something we want to do anyway to be able to monitor.

Discussion:
proxyPort           int socket              proxySocket timeout             time.Duration <ins> activeClients       *clientCache </ins> nodePort            int loadBalancerStatus  api.LoadBalancerStatus sessionAffinityType api.ServiceAffinity

protocol:            protocol, socket:              sock, timeout:             timeout, <ins> activeClients:       newClientCache(), </ins> sessionAffinityType: api.ServiceAffinityNone, // default stickyMaxAgeMinutes: 180,                     // TODO: parameterize this in the API. }

t.Errorf("expected %d ProxyLoops running, got %d", want, got) } <ins> func waitForNumProxyClients(t *testing.T, s *serviceInfo, want int, timeout time.Duration) { var got int now := time.Now() deadline := now.Add(timeout) for time.Now().Before(deadline) { s.activeClients.mu.Lock() got = len(s.activeClients.clients) s.activeClients.mu.Unlock() if got == want { return } time.Sleep(500 * time.Millisecond) } t.Errorf("expected %d ProxyClients live, got %d", want, got) } </ins> func TestTCPProxy(t *testing.T) { lb := NewLoadBalancerRR() service := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "testnamespace", Name: "echo"}, Port: "p"}

waitForNumProxyLoops(t, p, 1) } <ins> func TestUDPProxyTimeout(t *testing.T) { lb := NewLoadBalancerRR() service := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "testnamespace", Name: "echo"}, Port: "p"} lb.OnEndpointsUpdate([]api.Endpoints{ { ObjectMeta: api.ObjectMeta{Name: service.Name, Namespace: service.Namespace}, Subsets: []api.EndpointSubset{{ Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}}, Ports:     []api.EndpointPort{{Name: "p", Port: udpServerPort}}, }}, }, }) p, err := createProxier(lb, net.ParseIP("0.0.0.0"), &fakeIptables{}, net.ParseIP("127.0.0.1"), nil, time.Minute) if err != nil { t.Fatal(err) } 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) testEchoUDP(t, "127.0.0.1", svcInfo.proxyPort) // When connecting to a UDP service endpoint, there shoule be a Conn for proxy. waitForNumProxyClients(t, svcInfo, 1, time.Second) // If conn has no activity for serviceInfo.timeout since last Read/Write, it shoule be closed because of timeout. waitForNumProxyClients(t, svcInfo, 0, 2*time.Second) } </ins> func TestMultiPortProxy(t *testing.T) { lb := NewLoadBalancerRR() serviceP := proxy.ServicePortName{NamespacedName: types.NamespacedName{Namespace: "testnamespace", Name: "echo-p"}, Port: "p"}

waitForNumProxyLoops(t, p, 1) } <del> // TODO: Test UDP timeouts. </del> // TODO(justinsb): Add test for nodePort conflict detection, once we have nodePort wired in

} func (udp *udpProxySocket) ProxyLoop(service proxy.ServicePortName, myInfo *serviceInfo, proxier *Proxier) { <del> activeClients := newClientCache() </del> var buffer [4096]byte // 4KiB should be enough for most whole-packets for { if info, exists := proxier.getServiceInfo(service); !exists || info != myInfo {

break } // If this is a client we know already, reuse the connection and goroutine. <del> svrConn, err := udp.getBackendConn(activeClients, cliAddr, proxier, service, myInfo.timeout) </del> <ins> svrConn, err := udp.getBackendConn(myInfo.activeClients, cliAddr, proxier, service, myInfo.timeout) </ins> if err != nil { continue }