k8s.io/kubernetes@v1.29.3/pkg/controller/endpoint/endpoints_controller_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package endpoint
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"net/http"
    23  	"net/http/httptest"
    24  	"reflect"
    25  	"strconv"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/google/go-cmp/cmp"
    30  	v1 "k8s.io/api/core/v1"
    31  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    32  	"k8s.io/apimachinery/pkg/runtime"
    33  	"k8s.io/apimachinery/pkg/runtime/schema"
    34  	"k8s.io/apimachinery/pkg/util/intstr"
    35  	"k8s.io/apimachinery/pkg/util/wait"
    36  	"k8s.io/client-go/informers"
    37  	clientset "k8s.io/client-go/kubernetes"
    38  	"k8s.io/client-go/kubernetes/fake"
    39  	clientscheme "k8s.io/client-go/kubernetes/scheme"
    40  	restclient "k8s.io/client-go/rest"
    41  	"k8s.io/client-go/tools/cache"
    42  	utiltesting "k8s.io/client-go/util/testing"
    43  	endptspkg "k8s.io/kubernetes/pkg/api/v1/endpoints"
    44  	api "k8s.io/kubernetes/pkg/apis/core"
    45  	controllerpkg "k8s.io/kubernetes/pkg/controller"
    46  	utilnet "k8s.io/utils/net"
    47  	"k8s.io/utils/pointer"
    48  )
    49  
    50  var alwaysReady = func() bool { return true }
    51  var neverReady = func() bool { return false }
    52  var emptyNodeName string
    53  var triggerTime = time.Date(2018, 01, 01, 0, 0, 0, 0, time.UTC)
    54  var triggerTimeString = triggerTime.Format(time.RFC3339Nano)
    55  var oldTriggerTimeString = triggerTime.Add(-time.Hour).Format(time.RFC3339Nano)
    56  
    57  var ipv4only = []v1.IPFamily{v1.IPv4Protocol}
    58  var ipv6only = []v1.IPFamily{v1.IPv6Protocol}
    59  var ipv4ipv6 = []v1.IPFamily{v1.IPv4Protocol, v1.IPv6Protocol}
    60  var ipv6ipv4 = []v1.IPFamily{v1.IPv6Protocol, v1.IPv4Protocol}
    61  
    62  func testPod(namespace string, id int, nPorts int, isReady bool, ipFamilies []v1.IPFamily) *v1.Pod {
    63  	p := &v1.Pod{
    64  		TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
    65  		ObjectMeta: metav1.ObjectMeta{
    66  			Namespace:       namespace,
    67  			Name:            fmt.Sprintf("pod%d", id),
    68  			Labels:          map[string]string{"foo": "bar"},
    69  			ResourceVersion: fmt.Sprint(id),
    70  		},
    71  		Spec: v1.PodSpec{
    72  			Containers: []v1.Container{{Ports: []v1.ContainerPort{}}},
    73  		},
    74  		Status: v1.PodStatus{
    75  			Conditions: []v1.PodCondition{
    76  				{
    77  					Type:   v1.PodReady,
    78  					Status: v1.ConditionTrue,
    79  				},
    80  			},
    81  		},
    82  	}
    83  	if !isReady {
    84  		p.Status.Conditions[0].Status = v1.ConditionFalse
    85  	}
    86  	for j := 0; j < nPorts; j++ {
    87  		p.Spec.Containers[0].Ports = append(p.Spec.Containers[0].Ports,
    88  			v1.ContainerPort{Name: fmt.Sprintf("port%d", j), ContainerPort: int32(8080 + j)})
    89  	}
    90  	for _, family := range ipFamilies {
    91  		var ip string
    92  		if family == v1.IPv4Protocol {
    93  			ip = fmt.Sprintf("1.2.3.%d", 4+id)
    94  		} else {
    95  			ip = fmt.Sprintf("2000::%d", 4+id)
    96  		}
    97  		p.Status.PodIPs = append(p.Status.PodIPs, v1.PodIP{IP: ip})
    98  	}
    99  	p.Status.PodIP = p.Status.PodIPs[0].IP
   100  
   101  	return p
   102  }
   103  
   104  func addPods(store cache.Store, namespace string, nPods int, nPorts int, nNotReady int, ipFamilies []v1.IPFamily) {
   105  	for i := 0; i < nPods+nNotReady; i++ {
   106  		isReady := i < nPods
   107  		pod := testPod(namespace, i, nPorts, isReady, ipFamilies)
   108  		store.Add(pod)
   109  	}
   110  }
   111  
   112  func addNotReadyPodsWithSpecifiedRestartPolicyAndPhase(store cache.Store, namespace string, nPods int, nPorts int, restartPolicy v1.RestartPolicy, podPhase v1.PodPhase) {
   113  	for i := 0; i < nPods; i++ {
   114  		p := &v1.Pod{
   115  			TypeMeta: metav1.TypeMeta{APIVersion: "v1"},
   116  			ObjectMeta: metav1.ObjectMeta{
   117  				Namespace: namespace,
   118  				Name:      fmt.Sprintf("pod%d", i),
   119  				Labels:    map[string]string{"foo": "bar"},
   120  			},
   121  			Spec: v1.PodSpec{
   122  				RestartPolicy: restartPolicy,
   123  				Containers:    []v1.Container{{Ports: []v1.ContainerPort{}}},
   124  			},
   125  			Status: v1.PodStatus{
   126  				PodIP: fmt.Sprintf("1.2.3.%d", 4+i),
   127  				Phase: podPhase,
   128  				Conditions: []v1.PodCondition{
   129  					{
   130  						Type:   v1.PodReady,
   131  						Status: v1.ConditionFalse,
   132  					},
   133  				},
   134  			},
   135  		}
   136  		for j := 0; j < nPorts; j++ {
   137  			p.Spec.Containers[0].Ports = append(p.Spec.Containers[0].Ports,
   138  				v1.ContainerPort{Name: fmt.Sprintf("port%d", j), ContainerPort: int32(8080 + j)})
   139  		}
   140  		store.Add(p)
   141  	}
   142  }
   143  
   144  func makeTestServer(t *testing.T, namespace string) (*httptest.Server, *utiltesting.FakeHandler) {
   145  	fakeEndpointsHandler := utiltesting.FakeHandler{
   146  		StatusCode:   http.StatusOK,
   147  		ResponseBody: runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{}),
   148  	}
   149  	mux := http.NewServeMux()
   150  	if namespace == "" {
   151  		t.Fatal("namespace cannot be empty")
   152  	}
   153  	mux.Handle("/api/v1/namespaces/"+namespace+"/endpoints", &fakeEndpointsHandler)
   154  	mux.Handle("/api/v1/namespaces/"+namespace+"/endpoints/", &fakeEndpointsHandler)
   155  	mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
   156  		t.Errorf("unexpected request: %v", req.RequestURI)
   157  		http.Error(res, "", http.StatusNotFound)
   158  	})
   159  	return httptest.NewServer(mux), &fakeEndpointsHandler
   160  }
   161  
   162  // makeBlockingEndpointDeleteTestServer will signal the blockNextAction channel on endpoint "POST" & "DELETE" requests. All
   163  // block endpoint "DELETE" requestsi will wait on a blockDelete signal to delete endpoint. If controller is nil, a error will
   164  // be sent in the response.
   165  func makeBlockingEndpointDeleteTestServer(t *testing.T, controller *endpointController, endpoint *v1.Endpoints, blockDelete, blockNextAction chan struct{}, namespace string) *httptest.Server {
   166  
   167  	handlerFunc := func(res http.ResponseWriter, req *http.Request) {
   168  		if controller == nil {
   169  			res.WriteHeader(http.StatusInternalServerError)
   170  			res.Write([]byte("controller has not been set yet"))
   171  			return
   172  		}
   173  
   174  		if req.Method == "POST" {
   175  			controller.endpointsStore.Add(endpoint)
   176  			blockNextAction <- struct{}{}
   177  		}
   178  
   179  		if req.Method == "DELETE" {
   180  			go func() {
   181  				// Delay the deletion of endoints to make endpoint cache out of sync
   182  				<-blockDelete
   183  				controller.endpointsStore.Delete(endpoint)
   184  				controller.onEndpointsDelete(endpoint)
   185  			}()
   186  			blockNextAction <- struct{}{}
   187  		}
   188  
   189  		res.WriteHeader(http.StatusOK)
   190  		res.Write([]byte(runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{})))
   191  	}
   192  
   193  	mux := http.NewServeMux()
   194  	mux.HandleFunc("/api/v1/namespaces/"+namespace+"/endpoints", handlerFunc)
   195  	mux.HandleFunc("/api/v1/namespaces/"+namespace+"/endpoints/", handlerFunc)
   196  	mux.HandleFunc("/api/v1/namespaces/"+namespace+"/events", func(res http.ResponseWriter, req *http.Request) {})
   197  	mux.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {
   198  		t.Errorf("unexpected request: %v", req.RequestURI)
   199  		http.Error(res, "", http.StatusNotFound)
   200  	})
   201  	return httptest.NewServer(mux)
   202  
   203  }
   204  
   205  type endpointController struct {
   206  	*Controller
   207  	podStore       cache.Store
   208  	serviceStore   cache.Store
   209  	endpointsStore cache.Store
   210  }
   211  
   212  func newController(url string, batchPeriod time.Duration) *endpointController {
   213  	client := clientset.NewForConfigOrDie(&restclient.Config{Host: url, ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Group: "", Version: "v1"}}})
   214  	informerFactory := informers.NewSharedInformerFactory(client, controllerpkg.NoResyncPeriodFunc())
   215  	endpoints := NewEndpointController(informerFactory.Core().V1().Pods(), informerFactory.Core().V1().Services(),
   216  		informerFactory.Core().V1().Endpoints(), client, batchPeriod)
   217  	endpoints.podsSynced = alwaysReady
   218  	endpoints.servicesSynced = alwaysReady
   219  	endpoints.endpointsSynced = alwaysReady
   220  	return &endpointController{
   221  		endpoints,
   222  		informerFactory.Core().V1().Pods().Informer().GetStore(),
   223  		informerFactory.Core().V1().Services().Informer().GetStore(),
   224  		informerFactory.Core().V1().Endpoints().Informer().GetStore(),
   225  	}
   226  }
   227  
   228  func newFakeController(batchPeriod time.Duration) (*fake.Clientset, *endpointController) {
   229  	client := fake.NewSimpleClientset()
   230  	informerFactory := informers.NewSharedInformerFactory(client, controllerpkg.NoResyncPeriodFunc())
   231  
   232  	eController := NewEndpointController(
   233  		informerFactory.Core().V1().Pods(),
   234  		informerFactory.Core().V1().Services(),
   235  		informerFactory.Core().V1().Endpoints(),
   236  		client,
   237  		batchPeriod)
   238  
   239  	eController.podsSynced = alwaysReady
   240  	eController.servicesSynced = alwaysReady
   241  	eController.endpointsSynced = alwaysReady
   242  
   243  	return client, &endpointController{
   244  		eController,
   245  		informerFactory.Core().V1().Pods().Informer().GetStore(),
   246  		informerFactory.Core().V1().Services().Informer().GetStore(),
   247  		informerFactory.Core().V1().Endpoints().Informer().GetStore(),
   248  	}
   249  }
   250  
   251  func TestSyncEndpointsItemsPreserveNoSelector(t *testing.T) {
   252  	ns := metav1.NamespaceDefault
   253  	testServer, endpointsHandler := makeTestServer(t, ns)
   254  	defer testServer.Close()
   255  	endpoints := newController(testServer.URL, 0*time.Second)
   256  	endpoints.endpointsStore.Add(&v1.Endpoints{
   257  		ObjectMeta: metav1.ObjectMeta{
   258  			Name:            "foo",
   259  			Namespace:       ns,
   260  			ResourceVersion: "1",
   261  		},
   262  		Subsets: []v1.EndpointSubset{{
   263  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   264  			Ports:     []v1.EndpointPort{{Port: 1000}},
   265  		}},
   266  	})
   267  	endpoints.serviceStore.Add(&v1.Service{
   268  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   269  		Spec:       v1.ServiceSpec{Ports: []v1.ServicePort{{Port: 80}}},
   270  	})
   271  	endpoints.syncService(context.TODO(), ns+"/foo")
   272  	endpointsHandler.ValidateRequestCount(t, 0)
   273  }
   274  
   275  func TestSyncEndpointsExistingNilSubsets(t *testing.T) {
   276  	ns := metav1.NamespaceDefault
   277  	testServer, endpointsHandler := makeTestServer(t, ns)
   278  	defer testServer.Close()
   279  	endpoints := newController(testServer.URL, 0*time.Second)
   280  	endpoints.endpointsStore.Add(&v1.Endpoints{
   281  		ObjectMeta: metav1.ObjectMeta{
   282  			Name:            "foo",
   283  			Namespace:       ns,
   284  			ResourceVersion: "1",
   285  		},
   286  		Subsets: nil,
   287  	})
   288  	endpoints.serviceStore.Add(&v1.Service{
   289  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   290  		Spec: v1.ServiceSpec{
   291  			Selector: map[string]string{"foo": "bar"},
   292  			Ports:    []v1.ServicePort{{Port: 80}},
   293  		},
   294  	})
   295  	endpoints.syncService(context.TODO(), ns+"/foo")
   296  	endpointsHandler.ValidateRequestCount(t, 0)
   297  }
   298  
   299  func TestSyncEndpointsExistingEmptySubsets(t *testing.T) {
   300  	ns := metav1.NamespaceDefault
   301  	testServer, endpointsHandler := makeTestServer(t, ns)
   302  	defer testServer.Close()
   303  	endpoints := newController(testServer.URL, 0*time.Second)
   304  	endpoints.endpointsStore.Add(&v1.Endpoints{
   305  		ObjectMeta: metav1.ObjectMeta{
   306  			Name:            "foo",
   307  			Namespace:       ns,
   308  			ResourceVersion: "1",
   309  		},
   310  		Subsets: []v1.EndpointSubset{},
   311  	})
   312  	endpoints.serviceStore.Add(&v1.Service{
   313  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   314  		Spec: v1.ServiceSpec{
   315  			Selector: map[string]string{"foo": "bar"},
   316  			Ports:    []v1.ServicePort{{Port: 80}},
   317  		},
   318  	})
   319  	endpoints.syncService(context.TODO(), ns+"/foo")
   320  	endpointsHandler.ValidateRequestCount(t, 0)
   321  }
   322  
   323  func TestSyncEndpointsWithPodResourceVersionUpdateOnly(t *testing.T) {
   324  	ns := metav1.NamespaceDefault
   325  	testServer, endpointsHandler := makeTestServer(t, ns)
   326  	defer testServer.Close()
   327  	pod0 := testPod(ns, 0, 1, true, ipv4only)
   328  	pod1 := testPod(ns, 1, 1, false, ipv4only)
   329  	endpoints := newController(testServer.URL, 0*time.Second)
   330  	endpoints.endpointsStore.Add(&v1.Endpoints{
   331  		ObjectMeta: metav1.ObjectMeta{
   332  			Name:            "foo",
   333  			Namespace:       ns,
   334  			ResourceVersion: "1",
   335  		},
   336  		Subsets: []v1.EndpointSubset{{
   337  			Addresses: []v1.EndpointAddress{
   338  				{
   339  					IP:        pod0.Status.PodIPs[0].IP,
   340  					NodeName:  &emptyNodeName,
   341  					TargetRef: &v1.ObjectReference{Kind: "Pod", Name: pod0.Name, Namespace: ns, ResourceVersion: "1"},
   342  				},
   343  			},
   344  			NotReadyAddresses: []v1.EndpointAddress{
   345  				{
   346  					IP:        pod1.Status.PodIPs[0].IP,
   347  					NodeName:  &emptyNodeName,
   348  					TargetRef: &v1.ObjectReference{Kind: "Pod", Name: pod1.Name, Namespace: ns, ResourceVersion: "2"},
   349  				},
   350  			},
   351  			Ports: []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   352  		}},
   353  	})
   354  	endpoints.serviceStore.Add(&v1.Service{
   355  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   356  		Spec: v1.ServiceSpec{
   357  			Selector: map[string]string{"foo": "bar"},
   358  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   359  		},
   360  	})
   361  	pod0.ResourceVersion = "3"
   362  	pod1.ResourceVersion = "4"
   363  	endpoints.podStore.Add(pod0)
   364  	endpoints.podStore.Add(pod1)
   365  	endpoints.syncService(context.TODO(), ns+"/foo")
   366  	endpointsHandler.ValidateRequestCount(t, 0)
   367  }
   368  
   369  func TestSyncEndpointsNewNoSubsets(t *testing.T) {
   370  	ns := metav1.NamespaceDefault
   371  	testServer, endpointsHandler := makeTestServer(t, ns)
   372  	defer testServer.Close()
   373  	endpoints := newController(testServer.URL, 0*time.Second)
   374  	endpoints.serviceStore.Add(&v1.Service{
   375  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   376  		Spec: v1.ServiceSpec{
   377  			Selector: map[string]string{"foo": "bar"},
   378  			Ports:    []v1.ServicePort{{Port: 80}},
   379  		},
   380  	})
   381  	endpoints.syncService(context.TODO(), ns+"/foo")
   382  	endpointsHandler.ValidateRequestCount(t, 1)
   383  }
   384  
   385  func TestCheckLeftoverEndpoints(t *testing.T) {
   386  	ns := metav1.NamespaceDefault
   387  	testServer, _ := makeTestServer(t, ns)
   388  	defer testServer.Close()
   389  	endpoints := newController(testServer.URL, 0*time.Second)
   390  	endpoints.endpointsStore.Add(&v1.Endpoints{
   391  		ObjectMeta: metav1.ObjectMeta{
   392  			Name:            "foo",
   393  			Namespace:       ns,
   394  			ResourceVersion: "1",
   395  		},
   396  		Subsets: []v1.EndpointSubset{{
   397  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   398  			Ports:     []v1.EndpointPort{{Port: 1000}},
   399  		}},
   400  	})
   401  	endpoints.checkLeftoverEndpoints()
   402  	if e, a := 1, endpoints.queue.Len(); e != a {
   403  		t.Fatalf("Expected %v, got %v", e, a)
   404  	}
   405  	got, _ := endpoints.queue.Get()
   406  	if e, a := ns+"/foo", got; e != a {
   407  		t.Errorf("Expected %v, got %v", e, a)
   408  	}
   409  }
   410  
   411  func TestSyncEndpointsProtocolTCP(t *testing.T) {
   412  	ns := "other"
   413  	testServer, endpointsHandler := makeTestServer(t, ns)
   414  	defer testServer.Close()
   415  	endpoints := newController(testServer.URL, 0*time.Second)
   416  	endpoints.endpointsStore.Add(&v1.Endpoints{
   417  		ObjectMeta: metav1.ObjectMeta{
   418  			Name:            "foo",
   419  			Namespace:       ns,
   420  			ResourceVersion: "1",
   421  		},
   422  		Subsets: []v1.EndpointSubset{{
   423  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   424  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "TCP"}},
   425  		}},
   426  	})
   427  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   428  	endpoints.serviceStore.Add(&v1.Service{
   429  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   430  		Spec: v1.ServiceSpec{
   431  			Selector: map[string]string{},
   432  			Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "TCP"}},
   433  		},
   434  	})
   435  	endpoints.syncService(context.TODO(), ns+"/foo")
   436  
   437  	endpointsHandler.ValidateRequestCount(t, 1)
   438  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   439  		ObjectMeta: metav1.ObjectMeta{
   440  			Name:            "foo",
   441  			Namespace:       ns,
   442  			ResourceVersion: "1",
   443  			Labels: map[string]string{
   444  				v1.IsHeadlessService: "",
   445  			},
   446  		},
   447  		Subsets: []v1.EndpointSubset{{
   448  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   449  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   450  		}},
   451  	})
   452  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   453  }
   454  
   455  func TestSyncEndpointsHeadlessServiceLabel(t *testing.T) {
   456  	ns := metav1.NamespaceDefault
   457  	testServer, endpointsHandler := makeTestServer(t, ns)
   458  	defer testServer.Close()
   459  	endpoints := newController(testServer.URL, 0*time.Second)
   460  	endpoints.endpointsStore.Add(&v1.Endpoints{
   461  		ObjectMeta: metav1.ObjectMeta{
   462  			Name:            "foo",
   463  			Namespace:       ns,
   464  			ResourceVersion: "1",
   465  			Labels: map[string]string{
   466  				v1.IsHeadlessService: "",
   467  			},
   468  		},
   469  		Subsets: []v1.EndpointSubset{},
   470  	})
   471  	endpoints.serviceStore.Add(&v1.Service{
   472  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   473  		Spec: v1.ServiceSpec{
   474  			Selector: map[string]string{"foo": "bar"},
   475  			Ports:    []v1.ServicePort{{Port: 80}},
   476  		},
   477  	})
   478  	endpoints.syncService(context.TODO(), ns+"/foo")
   479  	endpointsHandler.ValidateRequestCount(t, 0)
   480  }
   481  
   482  func TestSyncServiceExternalNameType(t *testing.T) {
   483  	serviceName := "testing-1"
   484  	namespace := metav1.NamespaceDefault
   485  
   486  	testCases := []struct {
   487  		desc    string
   488  		service *v1.Service
   489  	}{
   490  		{
   491  			desc: "External name with selector and ports should not receive endpoints",
   492  			service: &v1.Service{
   493  				ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace},
   494  				Spec: v1.ServiceSpec{
   495  					Selector: map[string]string{"foo": "bar"},
   496  					Ports:    []v1.ServicePort{{Port: 80}},
   497  					Type:     v1.ServiceTypeExternalName,
   498  				},
   499  			},
   500  		},
   501  		{
   502  			desc: "External name with ports should not receive endpoints",
   503  			service: &v1.Service{
   504  				ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace},
   505  				Spec: v1.ServiceSpec{
   506  					Ports: []v1.ServicePort{{Port: 80}},
   507  					Type:  v1.ServiceTypeExternalName,
   508  				},
   509  			},
   510  		},
   511  		{
   512  			desc: "External name with selector should not receive endpoints",
   513  			service: &v1.Service{
   514  				ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace},
   515  				Spec: v1.ServiceSpec{
   516  					Selector: map[string]string{"foo": "bar"},
   517  					Type:     v1.ServiceTypeExternalName,
   518  				},
   519  			},
   520  		},
   521  		{
   522  			desc: "External name without selector and ports should not receive endpoints",
   523  			service: &v1.Service{
   524  				ObjectMeta: metav1.ObjectMeta{Name: serviceName, Namespace: namespace},
   525  				Spec: v1.ServiceSpec{
   526  					Type: v1.ServiceTypeExternalName,
   527  				},
   528  			},
   529  		},
   530  	}
   531  
   532  	for _, tc := range testCases {
   533  		t.Run(tc.desc, func(t *testing.T) {
   534  			testServer, endpointsHandler := makeTestServer(t, namespace)
   535  
   536  			defer testServer.Close()
   537  			endpoints := newController(testServer.URL, 0*time.Second)
   538  			err := endpoints.serviceStore.Add(tc.service)
   539  			if err != nil {
   540  				t.Fatalf("Error adding service to service store: %v", err)
   541  			}
   542  			err = endpoints.syncService(context.TODO(), namespace+"/"+serviceName)
   543  			if err != nil {
   544  				t.Fatalf("Error syncing service: %v", err)
   545  			}
   546  			endpointsHandler.ValidateRequestCount(t, 0)
   547  		})
   548  	}
   549  }
   550  
   551  func TestSyncEndpointsProtocolUDP(t *testing.T) {
   552  	ns := "other"
   553  	testServer, endpointsHandler := makeTestServer(t, ns)
   554  	defer testServer.Close()
   555  	endpoints := newController(testServer.URL, 0*time.Second)
   556  	endpoints.endpointsStore.Add(&v1.Endpoints{
   557  		ObjectMeta: metav1.ObjectMeta{
   558  			Name:            "foo",
   559  			Namespace:       ns,
   560  			ResourceVersion: "1",
   561  		},
   562  		Subsets: []v1.EndpointSubset{{
   563  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   564  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "UDP"}},
   565  		}},
   566  	})
   567  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   568  	endpoints.serviceStore.Add(&v1.Service{
   569  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   570  		Spec: v1.ServiceSpec{
   571  			Selector: map[string]string{},
   572  			Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "UDP"}},
   573  		},
   574  	})
   575  	endpoints.syncService(context.TODO(), ns+"/foo")
   576  
   577  	endpointsHandler.ValidateRequestCount(t, 1)
   578  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   579  		ObjectMeta: metav1.ObjectMeta{
   580  			Name:            "foo",
   581  			Namespace:       ns,
   582  			ResourceVersion: "1",
   583  			Labels: map[string]string{
   584  				v1.IsHeadlessService: "",
   585  			},
   586  		},
   587  		Subsets: []v1.EndpointSubset{{
   588  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   589  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "UDP"}},
   590  		}},
   591  	})
   592  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   593  }
   594  
   595  func TestSyncEndpointsProtocolSCTP(t *testing.T) {
   596  	ns := "other"
   597  	testServer, endpointsHandler := makeTestServer(t, ns)
   598  	defer testServer.Close()
   599  	endpoints := newController(testServer.URL, 0*time.Second)
   600  	endpoints.endpointsStore.Add(&v1.Endpoints{
   601  		ObjectMeta: metav1.ObjectMeta{
   602  			Name:            "foo",
   603  			Namespace:       ns,
   604  			ResourceVersion: "1",
   605  		},
   606  		Subsets: []v1.EndpointSubset{{
   607  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   608  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "SCTP"}},
   609  		}},
   610  	})
   611  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   612  	endpoints.serviceStore.Add(&v1.Service{
   613  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   614  		Spec: v1.ServiceSpec{
   615  			Selector: map[string]string{},
   616  			Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "SCTP"}},
   617  		},
   618  	})
   619  	endpoints.syncService(context.TODO(), ns+"/foo")
   620  
   621  	endpointsHandler.ValidateRequestCount(t, 1)
   622  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   623  		ObjectMeta: metav1.ObjectMeta{
   624  			Name:            "foo",
   625  			Namespace:       ns,
   626  			ResourceVersion: "1",
   627  			Labels: map[string]string{
   628  				v1.IsHeadlessService: "",
   629  			},
   630  		},
   631  		Subsets: []v1.EndpointSubset{{
   632  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   633  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "SCTP"}},
   634  		}},
   635  	})
   636  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   637  }
   638  
   639  func TestSyncEndpointsItemsEmptySelectorSelectsAll(t *testing.T) {
   640  	ns := "other"
   641  	testServer, endpointsHandler := makeTestServer(t, ns)
   642  	defer testServer.Close()
   643  	endpoints := newController(testServer.URL, 0*time.Second)
   644  	endpoints.endpointsStore.Add(&v1.Endpoints{
   645  		ObjectMeta: metav1.ObjectMeta{
   646  			Name:            "foo",
   647  			Namespace:       ns,
   648  			ResourceVersion: "1",
   649  		},
   650  		Subsets: []v1.EndpointSubset{},
   651  	})
   652  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   653  	endpoints.serviceStore.Add(&v1.Service{
   654  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   655  		Spec: v1.ServiceSpec{
   656  			Selector: map[string]string{},
   657  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   658  		},
   659  	})
   660  	endpoints.syncService(context.TODO(), ns+"/foo")
   661  
   662  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   663  		ObjectMeta: metav1.ObjectMeta{
   664  			Name:            "foo",
   665  			Namespace:       ns,
   666  			ResourceVersion: "1",
   667  			Labels: map[string]string{
   668  				v1.IsHeadlessService: "",
   669  			},
   670  		},
   671  		Subsets: []v1.EndpointSubset{{
   672  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   673  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   674  		}},
   675  	})
   676  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   677  }
   678  
   679  func TestSyncEndpointsItemsEmptySelectorSelectsAllNotReady(t *testing.T) {
   680  	ns := "other"
   681  	testServer, endpointsHandler := makeTestServer(t, ns)
   682  	defer testServer.Close()
   683  	endpoints := newController(testServer.URL, 0*time.Second)
   684  	endpoints.endpointsStore.Add(&v1.Endpoints{
   685  		ObjectMeta: metav1.ObjectMeta{
   686  			Name:            "foo",
   687  			Namespace:       ns,
   688  			ResourceVersion: "1",
   689  		},
   690  		Subsets: []v1.EndpointSubset{},
   691  	})
   692  	addPods(endpoints.podStore, ns, 0, 1, 1, ipv4only)
   693  	endpoints.serviceStore.Add(&v1.Service{
   694  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   695  		Spec: v1.ServiceSpec{
   696  			Selector: map[string]string{},
   697  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   698  		},
   699  	})
   700  	endpoints.syncService(context.TODO(), ns+"/foo")
   701  
   702  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   703  		ObjectMeta: metav1.ObjectMeta{
   704  			Name:            "foo",
   705  			Namespace:       ns,
   706  			ResourceVersion: "1",
   707  			Labels: map[string]string{
   708  				v1.IsHeadlessService: "",
   709  			},
   710  		},
   711  		Subsets: []v1.EndpointSubset{{
   712  			NotReadyAddresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   713  			Ports:             []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   714  		}},
   715  	})
   716  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   717  }
   718  
   719  func TestSyncEndpointsItemsEmptySelectorSelectsAllMixed(t *testing.T) {
   720  	ns := "other"
   721  	testServer, endpointsHandler := makeTestServer(t, ns)
   722  	defer testServer.Close()
   723  	endpoints := newController(testServer.URL, 0*time.Second)
   724  	endpoints.endpointsStore.Add(&v1.Endpoints{
   725  		ObjectMeta: metav1.ObjectMeta{
   726  			Name:            "foo",
   727  			Namespace:       ns,
   728  			ResourceVersion: "1",
   729  		},
   730  		Subsets: []v1.EndpointSubset{},
   731  	})
   732  	addPods(endpoints.podStore, ns, 1, 1, 1, ipv4only)
   733  	endpoints.serviceStore.Add(&v1.Service{
   734  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   735  		Spec: v1.ServiceSpec{
   736  			Selector: map[string]string{},
   737  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   738  		},
   739  	})
   740  	endpoints.syncService(context.TODO(), ns+"/foo")
   741  
   742  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   743  		ObjectMeta: metav1.ObjectMeta{
   744  			Name:            "foo",
   745  			Namespace:       ns,
   746  			ResourceVersion: "1",
   747  			Labels: map[string]string{
   748  				v1.IsHeadlessService: "",
   749  			},
   750  		},
   751  		Subsets: []v1.EndpointSubset{{
   752  			Addresses:         []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   753  			NotReadyAddresses: []v1.EndpointAddress{{IP: "1.2.3.5", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod1", Namespace: ns}}},
   754  			Ports:             []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   755  		}},
   756  	})
   757  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   758  }
   759  
   760  func TestSyncEndpointsItemsPreexisting(t *testing.T) {
   761  	ns := "bar"
   762  	testServer, endpointsHandler := makeTestServer(t, ns)
   763  	defer testServer.Close()
   764  	endpoints := newController(testServer.URL, 0*time.Second)
   765  	endpoints.endpointsStore.Add(&v1.Endpoints{
   766  		ObjectMeta: metav1.ObjectMeta{
   767  			Name:            "foo",
   768  			Namespace:       ns,
   769  			ResourceVersion: "1",
   770  		},
   771  		Subsets: []v1.EndpointSubset{{
   772  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   773  			Ports:     []v1.EndpointPort{{Port: 1000}},
   774  		}},
   775  	})
   776  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   777  	endpoints.serviceStore.Add(&v1.Service{
   778  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   779  		Spec: v1.ServiceSpec{
   780  			Selector: map[string]string{"foo": "bar"},
   781  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   782  		},
   783  	})
   784  	endpoints.syncService(context.TODO(), ns+"/foo")
   785  
   786  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   787  		ObjectMeta: metav1.ObjectMeta{
   788  			Name:            "foo",
   789  			Namespace:       ns,
   790  			ResourceVersion: "1",
   791  			Labels: map[string]string{
   792  				v1.IsHeadlessService: "",
   793  			},
   794  		},
   795  		Subsets: []v1.EndpointSubset{{
   796  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   797  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   798  		}},
   799  	})
   800  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   801  }
   802  
   803  func TestSyncEndpointsItemsPreexistingIdentical(t *testing.T) {
   804  	ns := metav1.NamespaceDefault
   805  	testServer, endpointsHandler := makeTestServer(t, ns)
   806  	defer testServer.Close()
   807  	endpoints := newController(testServer.URL, 0*time.Second)
   808  	endpoints.endpointsStore.Add(&v1.Endpoints{
   809  		ObjectMeta: metav1.ObjectMeta{
   810  			ResourceVersion: "1",
   811  			Name:            "foo",
   812  			Namespace:       ns,
   813  		},
   814  		Subsets: []v1.EndpointSubset{{
   815  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   816  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   817  		}},
   818  	})
   819  	addPods(endpoints.podStore, metav1.NamespaceDefault, 1, 1, 0, ipv4only)
   820  	endpoints.serviceStore.Add(&v1.Service{
   821  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: metav1.NamespaceDefault},
   822  		Spec: v1.ServiceSpec{
   823  			Selector: map[string]string{"foo": "bar"},
   824  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   825  		},
   826  	})
   827  	endpoints.syncService(context.TODO(), ns+"/foo")
   828  	endpointsHandler.ValidateRequestCount(t, 0)
   829  }
   830  
   831  func TestSyncEndpointsItems(t *testing.T) {
   832  	ns := "other"
   833  	testServer, endpointsHandler := makeTestServer(t, ns)
   834  	defer testServer.Close()
   835  	endpoints := newController(testServer.URL, 0*time.Second)
   836  	addPods(endpoints.podStore, ns, 3, 2, 0, ipv4only)
   837  	addPods(endpoints.podStore, "blah", 5, 2, 0, ipv4only) // make sure these aren't found!
   838  
   839  	endpoints.serviceStore.Add(&v1.Service{
   840  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   841  		Spec: v1.ServiceSpec{
   842  			Selector: map[string]string{"foo": "bar"},
   843  			Ports: []v1.ServicePort{
   844  				{Name: "port0", Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
   845  				{Name: "port1", Port: 88, Protocol: "TCP", TargetPort: intstr.FromInt32(8088)},
   846  			},
   847  		},
   848  	})
   849  	endpoints.syncService(context.TODO(), "other/foo")
   850  
   851  	expectedSubsets := []v1.EndpointSubset{{
   852  		Addresses: []v1.EndpointAddress{
   853  			{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}},
   854  			{IP: "1.2.3.5", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod1", Namespace: ns}},
   855  			{IP: "1.2.3.6", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod2", Namespace: ns}},
   856  		},
   857  		Ports: []v1.EndpointPort{
   858  			{Name: "port0", Port: 8080, Protocol: "TCP"},
   859  			{Name: "port1", Port: 8088, Protocol: "TCP"},
   860  		},
   861  	}}
   862  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   863  		ObjectMeta: metav1.ObjectMeta{
   864  			ResourceVersion: "",
   865  			Name:            "foo",
   866  			Labels: map[string]string{
   867  				v1.IsHeadlessService: "",
   868  			},
   869  		},
   870  		Subsets: endptspkg.SortSubsets(expectedSubsets),
   871  	})
   872  	endpointsHandler.ValidateRequestCount(t, 1)
   873  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints", "POST", &data)
   874  }
   875  
   876  func TestSyncEndpointsItemsWithLabels(t *testing.T) {
   877  	ns := "other"
   878  	testServer, endpointsHandler := makeTestServer(t, ns)
   879  	defer testServer.Close()
   880  	endpoints := newController(testServer.URL, 0*time.Second)
   881  	addPods(endpoints.podStore, ns, 3, 2, 0, ipv4only)
   882  	serviceLabels := map[string]string{"foo": "bar"}
   883  	endpoints.serviceStore.Add(&v1.Service{
   884  		ObjectMeta: metav1.ObjectMeta{
   885  			Name:      "foo",
   886  			Namespace: ns,
   887  			Labels:    serviceLabels,
   888  		},
   889  		Spec: v1.ServiceSpec{
   890  			Selector: map[string]string{"foo": "bar"},
   891  			Ports: []v1.ServicePort{
   892  				{Name: "port0", Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)},
   893  				{Name: "port1", Port: 88, Protocol: "TCP", TargetPort: intstr.FromInt32(8088)},
   894  			},
   895  		},
   896  	})
   897  	endpoints.syncService(context.TODO(), ns+"/foo")
   898  
   899  	expectedSubsets := []v1.EndpointSubset{{
   900  		Addresses: []v1.EndpointAddress{
   901  			{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}},
   902  			{IP: "1.2.3.5", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod1", Namespace: ns}},
   903  			{IP: "1.2.3.6", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod2", Namespace: ns}},
   904  		},
   905  		Ports: []v1.EndpointPort{
   906  			{Name: "port0", Port: 8080, Protocol: "TCP"},
   907  			{Name: "port1", Port: 8088, Protocol: "TCP"},
   908  		},
   909  	}}
   910  
   911  	serviceLabels[v1.IsHeadlessService] = ""
   912  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   913  		ObjectMeta: metav1.ObjectMeta{
   914  			ResourceVersion: "",
   915  			Name:            "foo",
   916  			Labels:          serviceLabels,
   917  		},
   918  		Subsets: endptspkg.SortSubsets(expectedSubsets),
   919  	})
   920  	endpointsHandler.ValidateRequestCount(t, 1)
   921  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints", "POST", &data)
   922  }
   923  
   924  func TestSyncEndpointsItemsPreexistingLabelsChange(t *testing.T) {
   925  	ns := "bar"
   926  	testServer, endpointsHandler := makeTestServer(t, ns)
   927  	defer testServer.Close()
   928  	endpoints := newController(testServer.URL, 0*time.Second)
   929  	endpoints.endpointsStore.Add(&v1.Endpoints{
   930  		ObjectMeta: metav1.ObjectMeta{
   931  			Name:            "foo",
   932  			Namespace:       ns,
   933  			ResourceVersion: "1",
   934  			Labels: map[string]string{
   935  				"foo": "bar",
   936  			},
   937  		},
   938  		Subsets: []v1.EndpointSubset{{
   939  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
   940  			Ports:     []v1.EndpointPort{{Port: 1000}},
   941  		}},
   942  	})
   943  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   944  	serviceLabels := map[string]string{"baz": "blah"}
   945  	endpoints.serviceStore.Add(&v1.Service{
   946  		ObjectMeta: metav1.ObjectMeta{
   947  			Name:      "foo",
   948  			Namespace: ns,
   949  			Labels:    serviceLabels,
   950  		},
   951  		Spec: v1.ServiceSpec{
   952  			Selector: map[string]string{"foo": "bar"},
   953  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
   954  		},
   955  	})
   956  	endpoints.syncService(context.TODO(), ns+"/foo")
   957  
   958  	serviceLabels[v1.IsHeadlessService] = ""
   959  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
   960  		ObjectMeta: metav1.ObjectMeta{
   961  			Name:            "foo",
   962  			Namespace:       ns,
   963  			ResourceVersion: "1",
   964  			Labels:          serviceLabels,
   965  		},
   966  		Subsets: []v1.EndpointSubset{{
   967  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
   968  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
   969  		}},
   970  	})
   971  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
   972  }
   973  
   974  func TestWaitsForAllInformersToBeSynced2(t *testing.T) {
   975  	var tests = []struct {
   976  		podsSynced            func() bool
   977  		servicesSynced        func() bool
   978  		endpointsSynced       func() bool
   979  		shouldUpdateEndpoints bool
   980  	}{
   981  		{neverReady, alwaysReady, alwaysReady, false},
   982  		{alwaysReady, neverReady, alwaysReady, false},
   983  		{alwaysReady, alwaysReady, neverReady, false},
   984  		{alwaysReady, alwaysReady, alwaysReady, true},
   985  	}
   986  
   987  	for _, test := range tests {
   988  		func() {
   989  			ns := "other"
   990  			testServer, endpointsHandler := makeTestServer(t, ns)
   991  			defer testServer.Close()
   992  			endpoints := newController(testServer.URL, 0*time.Second)
   993  			addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
   994  
   995  			service := &v1.Service{
   996  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
   997  				Spec: v1.ServiceSpec{
   998  					Selector: map[string]string{},
   999  					Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "TCP"}},
  1000  				},
  1001  			}
  1002  			endpoints.serviceStore.Add(service)
  1003  			endpoints.onServiceUpdate(service)
  1004  			endpoints.podsSynced = test.podsSynced
  1005  			endpoints.servicesSynced = test.servicesSynced
  1006  			endpoints.endpointsSynced = test.endpointsSynced
  1007  			endpoints.workerLoopPeriod = 10 * time.Millisecond
  1008  			stopCh := make(chan struct{})
  1009  			defer close(stopCh)
  1010  			go endpoints.Run(context.TODO(), 1)
  1011  
  1012  			// cache.WaitForNamedCacheSync has a 100ms poll period, and the endpoints worker has a 10ms period.
  1013  			// To ensure we get all updates, including unexpected ones, we need to wait at least as long as
  1014  			// a single cache sync period and worker period, with some fudge room.
  1015  			time.Sleep(150 * time.Millisecond)
  1016  			if test.shouldUpdateEndpoints {
  1017  				// Ensure the work queue has been processed by looping for up to a second to prevent flakes.
  1018  				wait.PollImmediate(50*time.Millisecond, 1*time.Second, func() (bool, error) {
  1019  					return endpoints.queue.Len() == 0, nil
  1020  				})
  1021  				endpointsHandler.ValidateRequestCount(t, 1)
  1022  			} else {
  1023  				endpointsHandler.ValidateRequestCount(t, 0)
  1024  			}
  1025  		}()
  1026  	}
  1027  }
  1028  
  1029  func TestSyncEndpointsHeadlessService(t *testing.T) {
  1030  	ns := "headless"
  1031  	testServer, endpointsHandler := makeTestServer(t, ns)
  1032  	defer testServer.Close()
  1033  	endpoints := newController(testServer.URL, 0*time.Second)
  1034  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1035  		ObjectMeta: metav1.ObjectMeta{
  1036  			Name:            "foo",
  1037  			Namespace:       ns,
  1038  			ResourceVersion: "1",
  1039  		},
  1040  		Subsets: []v1.EndpointSubset{{
  1041  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
  1042  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "TCP"}},
  1043  		}},
  1044  	})
  1045  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
  1046  	service := &v1.Service{
  1047  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns, Labels: map[string]string{"a": "b"}},
  1048  		Spec: v1.ServiceSpec{
  1049  			Selector:  map[string]string{},
  1050  			ClusterIP: api.ClusterIPNone,
  1051  			Ports:     []v1.ServicePort{},
  1052  		},
  1053  	}
  1054  	originalService := service.DeepCopy()
  1055  	endpoints.serviceStore.Add(service)
  1056  	endpoints.syncService(context.TODO(), ns+"/foo")
  1057  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1058  		ObjectMeta: metav1.ObjectMeta{
  1059  			Name:            "foo",
  1060  			Namespace:       ns,
  1061  			ResourceVersion: "1",
  1062  			Labels: map[string]string{
  1063  				"a":                  "b",
  1064  				v1.IsHeadlessService: "",
  1065  			},
  1066  		},
  1067  		Subsets: []v1.EndpointSubset{{
  1068  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
  1069  			Ports:     []v1.EndpointPort{},
  1070  		}},
  1071  	})
  1072  	if !reflect.DeepEqual(originalService, service) {
  1073  		t.Fatalf("syncing endpoints changed service: %s", cmp.Diff(service, originalService))
  1074  	}
  1075  	endpointsHandler.ValidateRequestCount(t, 1)
  1076  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1077  }
  1078  
  1079  func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseFailed(t *testing.T) {
  1080  	ns := "other"
  1081  	testServer, endpointsHandler := makeTestServer(t, ns)
  1082  	defer testServer.Close()
  1083  	endpoints := newController(testServer.URL, 0*time.Second)
  1084  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1085  		ObjectMeta: metav1.ObjectMeta{
  1086  			Name:            "foo",
  1087  			Namespace:       ns,
  1088  			ResourceVersion: "1",
  1089  			Labels: map[string]string{
  1090  				"foo": "bar",
  1091  			},
  1092  		},
  1093  		Subsets: []v1.EndpointSubset{},
  1094  	})
  1095  	addNotReadyPodsWithSpecifiedRestartPolicyAndPhase(endpoints.podStore, ns, 1, 1, v1.RestartPolicyNever, v1.PodFailed)
  1096  	endpoints.serviceStore.Add(&v1.Service{
  1097  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1098  		Spec: v1.ServiceSpec{
  1099  			Selector: map[string]string{"foo": "bar"},
  1100  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
  1101  		},
  1102  	})
  1103  	endpoints.syncService(context.TODO(), ns+"/foo")
  1104  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1105  		ObjectMeta: metav1.ObjectMeta{
  1106  			Name:            "foo",
  1107  			Namespace:       ns,
  1108  			ResourceVersion: "1",
  1109  			Labels: map[string]string{
  1110  				v1.IsHeadlessService: "",
  1111  			},
  1112  		},
  1113  		Subsets: []v1.EndpointSubset{},
  1114  	})
  1115  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1116  }
  1117  
  1118  func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseSucceeded(t *testing.T) {
  1119  	ns := "other"
  1120  	testServer, endpointsHandler := makeTestServer(t, ns)
  1121  	defer testServer.Close()
  1122  	endpoints := newController(testServer.URL, 0*time.Second)
  1123  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1124  		ObjectMeta: metav1.ObjectMeta{
  1125  			Name:            "foo",
  1126  			Namespace:       ns,
  1127  			ResourceVersion: "1",
  1128  			Labels: map[string]string{
  1129  				"foo": "bar",
  1130  			},
  1131  		},
  1132  		Subsets: []v1.EndpointSubset{},
  1133  	})
  1134  	addNotReadyPodsWithSpecifiedRestartPolicyAndPhase(endpoints.podStore, ns, 1, 1, v1.RestartPolicyNever, v1.PodSucceeded)
  1135  	endpoints.serviceStore.Add(&v1.Service{
  1136  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1137  		Spec: v1.ServiceSpec{
  1138  			Selector: map[string]string{"foo": "bar"},
  1139  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
  1140  		},
  1141  	})
  1142  	endpoints.syncService(context.TODO(), ns+"/foo")
  1143  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1144  		ObjectMeta: metav1.ObjectMeta{
  1145  			Name:            "foo",
  1146  			Namespace:       ns,
  1147  			ResourceVersion: "1",
  1148  			Labels: map[string]string{
  1149  				v1.IsHeadlessService: "",
  1150  			},
  1151  		},
  1152  		Subsets: []v1.EndpointSubset{},
  1153  	})
  1154  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1155  }
  1156  
  1157  func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyOnFailureAndPhaseSucceeded(t *testing.T) {
  1158  	ns := "other"
  1159  	testServer, endpointsHandler := makeTestServer(t, ns)
  1160  	defer testServer.Close()
  1161  	endpoints := newController(testServer.URL, 0*time.Second)
  1162  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1163  		ObjectMeta: metav1.ObjectMeta{
  1164  			Name:            "foo",
  1165  			Namespace:       ns,
  1166  			ResourceVersion: "1",
  1167  			Labels: map[string]string{
  1168  				"foo": "bar",
  1169  			},
  1170  		},
  1171  		Subsets: []v1.EndpointSubset{},
  1172  	})
  1173  	addNotReadyPodsWithSpecifiedRestartPolicyAndPhase(endpoints.podStore, ns, 1, 1, v1.RestartPolicyOnFailure, v1.PodSucceeded)
  1174  	endpoints.serviceStore.Add(&v1.Service{
  1175  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1176  		Spec: v1.ServiceSpec{
  1177  			Selector: map[string]string{"foo": "bar"},
  1178  			Ports:    []v1.ServicePort{{Port: 80, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)}},
  1179  		},
  1180  	})
  1181  	endpoints.syncService(context.TODO(), ns+"/foo")
  1182  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1183  		ObjectMeta: metav1.ObjectMeta{
  1184  			Name:            "foo",
  1185  			Namespace:       ns,
  1186  			ResourceVersion: "1",
  1187  			Labels: map[string]string{
  1188  				v1.IsHeadlessService: "",
  1189  			},
  1190  		},
  1191  		Subsets: []v1.EndpointSubset{},
  1192  	})
  1193  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1194  }
  1195  
  1196  func TestSyncEndpointsHeadlessWithoutPort(t *testing.T) {
  1197  	ns := metav1.NamespaceDefault
  1198  	testServer, endpointsHandler := makeTestServer(t, ns)
  1199  	defer testServer.Close()
  1200  	endpoints := newController(testServer.URL, 0*time.Second)
  1201  	endpoints.serviceStore.Add(&v1.Service{
  1202  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1203  		Spec: v1.ServiceSpec{
  1204  			Selector:  map[string]string{"foo": "bar"},
  1205  			ClusterIP: "None",
  1206  			Ports:     nil,
  1207  		},
  1208  	})
  1209  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
  1210  	endpoints.syncService(context.TODO(), ns+"/foo")
  1211  	endpointsHandler.ValidateRequestCount(t, 1)
  1212  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1213  		ObjectMeta: metav1.ObjectMeta{
  1214  			Name: "foo",
  1215  			Labels: map[string]string{
  1216  				v1.IsHeadlessService: "",
  1217  			},
  1218  		},
  1219  		Subsets: []v1.EndpointSubset{{
  1220  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
  1221  			Ports:     nil,
  1222  		}},
  1223  	})
  1224  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints", "POST", &data)
  1225  }
  1226  
  1227  func TestPodToEndpointAddressForService(t *testing.T) {
  1228  	ipv4 := v1.IPv4Protocol
  1229  	ipv6 := v1.IPv6Protocol
  1230  
  1231  	testCases := []struct {
  1232  		name                   string
  1233  		ipFamilies             []v1.IPFamily
  1234  		service                v1.Service
  1235  		expectedEndpointFamily v1.IPFamily
  1236  		expectError            bool
  1237  	}{
  1238  		{
  1239  			name:       "v4 service, in a single stack cluster",
  1240  			ipFamilies: ipv4only,
  1241  			service: v1.Service{
  1242  				Spec: v1.ServiceSpec{
  1243  					ClusterIP: "10.0.0.1",
  1244  				},
  1245  			},
  1246  			expectedEndpointFamily: ipv4,
  1247  		},
  1248  		{
  1249  			name:       "v4 service, in a dual stack cluster",
  1250  			ipFamilies: ipv4ipv6,
  1251  			service: v1.Service{
  1252  				Spec: v1.ServiceSpec{
  1253  					ClusterIP: "10.0.0.1",
  1254  				},
  1255  			},
  1256  			expectedEndpointFamily: ipv4,
  1257  		},
  1258  		{
  1259  			name:       "v4 service, in a dual stack ipv6-primary cluster",
  1260  			ipFamilies: ipv6ipv4,
  1261  			service: v1.Service{
  1262  				Spec: v1.ServiceSpec{
  1263  					ClusterIP: "10.0.0.1",
  1264  				},
  1265  			},
  1266  			expectedEndpointFamily: ipv4,
  1267  		},
  1268  		{
  1269  			name:       "v4 headless service, in a single stack cluster",
  1270  			ipFamilies: ipv4only,
  1271  			service: v1.Service{
  1272  				Spec: v1.ServiceSpec{
  1273  					ClusterIP: v1.ClusterIPNone,
  1274  				},
  1275  			},
  1276  			expectedEndpointFamily: ipv4,
  1277  		},
  1278  		{
  1279  			name:       "v4 headless service, in a dual stack cluster",
  1280  			ipFamilies: ipv4ipv6,
  1281  			service: v1.Service{
  1282  				Spec: v1.ServiceSpec{
  1283  					ClusterIP:  v1.ClusterIPNone,
  1284  					IPFamilies: []v1.IPFamily{v1.IPv4Protocol},
  1285  				},
  1286  			},
  1287  			expectedEndpointFamily: ipv4,
  1288  		},
  1289  		{
  1290  			name:       "v4 legacy headless service, in a dual stack cluster",
  1291  			ipFamilies: ipv4ipv6,
  1292  			service: v1.Service{
  1293  				Spec: v1.ServiceSpec{
  1294  					ClusterIP: v1.ClusterIPNone,
  1295  				},
  1296  			},
  1297  			expectedEndpointFamily: ipv4,
  1298  		},
  1299  		{
  1300  			name:       "v4 legacy headless service, in a dual stack ipv6-primary cluster",
  1301  			ipFamilies: ipv6ipv4,
  1302  			service: v1.Service{
  1303  				Spec: v1.ServiceSpec{
  1304  					ClusterIP: v1.ClusterIPNone,
  1305  				},
  1306  			},
  1307  			expectedEndpointFamily: ipv6,
  1308  		},
  1309  		{
  1310  			name:       "v6 service, in a dual stack cluster",
  1311  			ipFamilies: ipv4ipv6,
  1312  			service: v1.Service{
  1313  				Spec: v1.ServiceSpec{
  1314  					ClusterIP: "3000::1",
  1315  				},
  1316  			},
  1317  			expectedEndpointFamily: ipv6,
  1318  		},
  1319  		{
  1320  			name:       "v6 headless service, in a single stack cluster",
  1321  			ipFamilies: ipv6only,
  1322  			service: v1.Service{
  1323  				Spec: v1.ServiceSpec{
  1324  					ClusterIP: v1.ClusterIPNone,
  1325  				},
  1326  			},
  1327  			expectedEndpointFamily: ipv6,
  1328  		},
  1329  		{
  1330  			name:       "v6 headless service, in a dual stack cluster (connected to a new api-server)",
  1331  			ipFamilies: ipv4ipv6,
  1332  			service: v1.Service{
  1333  				Spec: v1.ServiceSpec{
  1334  					ClusterIP:  v1.ClusterIPNone,
  1335  					IPFamilies: []v1.IPFamily{v1.IPv6Protocol}, // <- set by a api-server defaulting logic
  1336  				},
  1337  			},
  1338  			expectedEndpointFamily: ipv6,
  1339  		},
  1340  		{
  1341  			name:       "v6 legacy headless service, in a dual stack cluster  (connected to a old api-server)",
  1342  			ipFamilies: ipv4ipv6,
  1343  			service: v1.Service{
  1344  				Spec: v1.ServiceSpec{
  1345  					ClusterIP: v1.ClusterIPNone, // <- families are not set by api-server
  1346  				},
  1347  			},
  1348  			expectedEndpointFamily: ipv4,
  1349  		},
  1350  		// in reality this is a misconfigured cluster
  1351  		// i.e user is not using dual stack and have PodIP == v4 and ServiceIP==v6
  1352  		// previously controller could assign wrong ip to endpoint address
  1353  		// with gate removed. this is no longer the case. this is *not* behavior change
  1354  		// because previously things would have failed in kube-proxy anyway (due to editing wrong iptables).
  1355  		{
  1356  			name:       "v6 service, in a v4 only cluster.",
  1357  			ipFamilies: ipv4only,
  1358  			service: v1.Service{
  1359  				Spec: v1.ServiceSpec{
  1360  					ClusterIP: "3000::1",
  1361  				},
  1362  			},
  1363  			expectError:            true,
  1364  			expectedEndpointFamily: ipv4,
  1365  		},
  1366  		// but this will actually give an error
  1367  		{
  1368  			name:       "v6 service, in a v4 only cluster",
  1369  			ipFamilies: ipv4only,
  1370  			service: v1.Service{
  1371  				Spec: v1.ServiceSpec{
  1372  					ClusterIP: "3000::1",
  1373  				},
  1374  			},
  1375  			expectError: true,
  1376  		},
  1377  	}
  1378  	for _, tc := range testCases {
  1379  		t.Run(tc.name, func(t *testing.T) {
  1380  			podStore := cache.NewStore(cache.DeletionHandlingMetaNamespaceKeyFunc)
  1381  			ns := "test"
  1382  			addPods(podStore, ns, 1, 1, 0, tc.ipFamilies)
  1383  			pods := podStore.List()
  1384  			if len(pods) != 1 {
  1385  				t.Fatalf("podStore size: expected: %d, got: %d", 1, len(pods))
  1386  			}
  1387  			pod := pods[0].(*v1.Pod)
  1388  			epa, err := podToEndpointAddressForService(&tc.service, pod)
  1389  
  1390  			if err != nil && !tc.expectError {
  1391  				t.Fatalf("podToEndpointAddressForService returned unexpected error %v", err)
  1392  			}
  1393  
  1394  			if err == nil && tc.expectError {
  1395  				t.Fatalf("podToEndpointAddressForService should have returned error but it did not")
  1396  			}
  1397  
  1398  			if err != nil && tc.expectError {
  1399  				return
  1400  			}
  1401  
  1402  			if utilnet.IsIPv6String(epa.IP) != (tc.expectedEndpointFamily == ipv6) {
  1403  				t.Fatalf("IP: expected %s, got: %s", tc.expectedEndpointFamily, epa.IP)
  1404  			}
  1405  			if *(epa.NodeName) != pod.Spec.NodeName {
  1406  				t.Fatalf("NodeName: expected: %s, got: %s", pod.Spec.NodeName, *(epa.NodeName))
  1407  			}
  1408  			if epa.TargetRef.Kind != "Pod" {
  1409  				t.Fatalf("TargetRef.Kind: expected: %s, got: %s", "Pod", epa.TargetRef.Kind)
  1410  			}
  1411  			if epa.TargetRef.Namespace != pod.ObjectMeta.Namespace {
  1412  				t.Fatalf("TargetRef.Namespace: expected: %s, got: %s", pod.ObjectMeta.Namespace, epa.TargetRef.Namespace)
  1413  			}
  1414  			if epa.TargetRef.Name != pod.ObjectMeta.Name {
  1415  				t.Fatalf("TargetRef.Name: expected: %s, got: %s", pod.ObjectMeta.Name, epa.TargetRef.Name)
  1416  			}
  1417  			if epa.TargetRef.UID != pod.ObjectMeta.UID {
  1418  				t.Fatalf("TargetRef.UID: expected: %s, got: %s", pod.ObjectMeta.UID, epa.TargetRef.UID)
  1419  			}
  1420  			if epa.TargetRef.ResourceVersion != "" {
  1421  				t.Fatalf("TargetRef.ResourceVersion: expected empty, got: %s", epa.TargetRef.ResourceVersion)
  1422  			}
  1423  		})
  1424  	}
  1425  
  1426  }
  1427  
  1428  func TestLastTriggerChangeTimeAnnotation(t *testing.T) {
  1429  	ns := "other"
  1430  	testServer, endpointsHandler := makeTestServer(t, ns)
  1431  	defer testServer.Close()
  1432  	endpoints := newController(testServer.URL, 0*time.Second)
  1433  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1434  		ObjectMeta: metav1.ObjectMeta{
  1435  			Name:            "foo",
  1436  			Namespace:       ns,
  1437  			ResourceVersion: "1",
  1438  		},
  1439  		Subsets: []v1.EndpointSubset{{
  1440  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
  1441  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "TCP"}},
  1442  		}},
  1443  	})
  1444  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
  1445  	endpoints.serviceStore.Add(&v1.Service{
  1446  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns, CreationTimestamp: metav1.NewTime(triggerTime)},
  1447  		Spec: v1.ServiceSpec{
  1448  			Selector: map[string]string{},
  1449  			Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "TCP"}},
  1450  		},
  1451  	})
  1452  	endpoints.syncService(context.TODO(), ns+"/foo")
  1453  
  1454  	endpointsHandler.ValidateRequestCount(t, 1)
  1455  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1456  		ObjectMeta: metav1.ObjectMeta{
  1457  			Name:            "foo",
  1458  			Namespace:       ns,
  1459  			ResourceVersion: "1",
  1460  			Annotations: map[string]string{
  1461  				v1.EndpointsLastChangeTriggerTime: triggerTimeString,
  1462  			},
  1463  			Labels: map[string]string{
  1464  				v1.IsHeadlessService: "",
  1465  			},
  1466  		},
  1467  		Subsets: []v1.EndpointSubset{{
  1468  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
  1469  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
  1470  		}},
  1471  	})
  1472  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1473  }
  1474  
  1475  func TestLastTriggerChangeTimeAnnotation_AnnotationOverridden(t *testing.T) {
  1476  	ns := "other"
  1477  	testServer, endpointsHandler := makeTestServer(t, ns)
  1478  	defer testServer.Close()
  1479  	endpoints := newController(testServer.URL, 0*time.Second)
  1480  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1481  		ObjectMeta: metav1.ObjectMeta{
  1482  			Name:            "foo",
  1483  			Namespace:       ns,
  1484  			ResourceVersion: "1",
  1485  			Annotations: map[string]string{
  1486  				v1.EndpointsLastChangeTriggerTime: oldTriggerTimeString,
  1487  			},
  1488  		},
  1489  		Subsets: []v1.EndpointSubset{{
  1490  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
  1491  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "TCP"}},
  1492  		}},
  1493  	})
  1494  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
  1495  	endpoints.serviceStore.Add(&v1.Service{
  1496  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns, CreationTimestamp: metav1.NewTime(triggerTime)},
  1497  		Spec: v1.ServiceSpec{
  1498  			Selector: map[string]string{},
  1499  			Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "TCP"}},
  1500  		},
  1501  	})
  1502  	endpoints.syncService(context.TODO(), ns+"/foo")
  1503  
  1504  	endpointsHandler.ValidateRequestCount(t, 1)
  1505  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1506  		ObjectMeta: metav1.ObjectMeta{
  1507  			Name:            "foo",
  1508  			Namespace:       ns,
  1509  			ResourceVersion: "1",
  1510  			Annotations: map[string]string{
  1511  				v1.EndpointsLastChangeTriggerTime: triggerTimeString,
  1512  			},
  1513  			Labels: map[string]string{
  1514  				v1.IsHeadlessService: "",
  1515  			},
  1516  		},
  1517  		Subsets: []v1.EndpointSubset{{
  1518  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
  1519  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
  1520  		}},
  1521  	})
  1522  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1523  }
  1524  
  1525  func TestLastTriggerChangeTimeAnnotation_AnnotationCleared(t *testing.T) {
  1526  	ns := "other"
  1527  	testServer, endpointsHandler := makeTestServer(t, ns)
  1528  	defer testServer.Close()
  1529  	endpoints := newController(testServer.URL, 0*time.Second)
  1530  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1531  		ObjectMeta: metav1.ObjectMeta{
  1532  			Name:            "foo",
  1533  			Namespace:       ns,
  1534  			ResourceVersion: "1",
  1535  			Annotations: map[string]string{
  1536  				v1.EndpointsLastChangeTriggerTime: triggerTimeString,
  1537  			},
  1538  		},
  1539  		Subsets: []v1.EndpointSubset{{
  1540  			Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
  1541  			Ports:     []v1.EndpointPort{{Port: 1000, Protocol: "TCP"}},
  1542  		}},
  1543  	})
  1544  	// Neither pod nor service has trigger time, this should cause annotation to be cleared.
  1545  	addPods(endpoints.podStore, ns, 1, 1, 0, ipv4only)
  1546  	endpoints.serviceStore.Add(&v1.Service{
  1547  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1548  		Spec: v1.ServiceSpec{
  1549  			Selector: map[string]string{},
  1550  			Ports:    []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt32(8080), Protocol: "TCP"}},
  1551  		},
  1552  	})
  1553  	endpoints.syncService(context.TODO(), ns+"/foo")
  1554  
  1555  	endpointsHandler.ValidateRequestCount(t, 1)
  1556  	data := runtime.EncodeOrDie(clientscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), &v1.Endpoints{
  1557  		ObjectMeta: metav1.ObjectMeta{
  1558  			Name:            "foo",
  1559  			Namespace:       ns,
  1560  			ResourceVersion: "1",
  1561  			Labels: map[string]string{
  1562  				v1.IsHeadlessService: "",
  1563  			}, // Annotation not set anymore.
  1564  		},
  1565  		Subsets: []v1.EndpointSubset{{
  1566  			Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
  1567  			Ports:     []v1.EndpointPort{{Port: 8080, Protocol: "TCP"}},
  1568  		}},
  1569  	})
  1570  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "PUT", &data)
  1571  }
  1572  
  1573  // TestPodUpdatesBatching verifies that endpoint updates caused by pod updates are batched together.
  1574  // This test uses real time.Sleep, as there is no easy way to mock time in endpoints controller now.
  1575  // TODO(mborsz): Migrate this test to mock clock when possible.
  1576  func TestPodUpdatesBatching(t *testing.T) {
  1577  	type podUpdate struct {
  1578  		delay   time.Duration
  1579  		podName string
  1580  		podIP   string
  1581  	}
  1582  
  1583  	tests := []struct {
  1584  		name             string
  1585  		batchPeriod      time.Duration
  1586  		podsCount        int
  1587  		updates          []podUpdate
  1588  		finalDelay       time.Duration
  1589  		wantRequestCount int
  1590  	}{
  1591  		{
  1592  			name:        "three updates with no batching",
  1593  			batchPeriod: 0 * time.Second,
  1594  			podsCount:   10,
  1595  			updates: []podUpdate{
  1596  				{
  1597  					// endpoints.Run needs ~100 ms to start processing updates.
  1598  					delay:   200 * time.Millisecond,
  1599  					podName: "pod0",
  1600  					podIP:   "10.0.0.0",
  1601  				},
  1602  				{
  1603  					delay:   100 * time.Millisecond,
  1604  					podName: "pod1",
  1605  					podIP:   "10.0.0.1",
  1606  				},
  1607  				{
  1608  					delay:   100 * time.Millisecond,
  1609  					podName: "pod2",
  1610  					podIP:   "10.0.0.2",
  1611  				},
  1612  			},
  1613  			finalDelay:       3 * time.Second,
  1614  			wantRequestCount: 3,
  1615  		},
  1616  		{
  1617  			name:        "three updates in one batch",
  1618  			batchPeriod: 1 * time.Second,
  1619  			podsCount:   10,
  1620  			updates: []podUpdate{
  1621  				{
  1622  					// endpoints.Run needs ~100 ms to start processing updates.
  1623  					delay:   200 * time.Millisecond,
  1624  					podName: "pod0",
  1625  					podIP:   "10.0.0.0",
  1626  				},
  1627  				{
  1628  					delay:   100 * time.Millisecond,
  1629  					podName: "pod1",
  1630  					podIP:   "10.0.0.1",
  1631  				},
  1632  				{
  1633  					delay:   100 * time.Millisecond,
  1634  					podName: "pod2",
  1635  					podIP:   "10.0.0.2",
  1636  				},
  1637  			},
  1638  			finalDelay:       3 * time.Second,
  1639  			wantRequestCount: 1,
  1640  		},
  1641  		{
  1642  			name:        "three updates in two batches",
  1643  			batchPeriod: 1 * time.Second,
  1644  			podsCount:   10,
  1645  			updates: []podUpdate{
  1646  				{
  1647  					// endpoints.Run needs ~100 ms to start processing updates.
  1648  					delay:   200 * time.Millisecond,
  1649  					podName: "pod0",
  1650  					podIP:   "10.0.0.0",
  1651  				},
  1652  				{
  1653  					delay:   100 * time.Millisecond,
  1654  					podName: "pod1",
  1655  					podIP:   "10.0.0.1",
  1656  				},
  1657  				{
  1658  					delay:   1 * time.Second,
  1659  					podName: "pod2",
  1660  					podIP:   "10.0.0.2",
  1661  				},
  1662  			},
  1663  			finalDelay:       3 * time.Second,
  1664  			wantRequestCount: 2,
  1665  		},
  1666  	}
  1667  
  1668  	for _, tc := range tests {
  1669  		t.Run(tc.name, func(t *testing.T) {
  1670  			ns := "other"
  1671  			resourceVersion := 1
  1672  			testServer, endpointsHandler := makeTestServer(t, ns)
  1673  			defer testServer.Close()
  1674  			endpoints := newController(testServer.URL, tc.batchPeriod)
  1675  			stopCh := make(chan struct{})
  1676  			defer close(stopCh)
  1677  			endpoints.podsSynced = alwaysReady
  1678  			endpoints.servicesSynced = alwaysReady
  1679  			endpoints.endpointsSynced = alwaysReady
  1680  			endpoints.workerLoopPeriod = 10 * time.Millisecond
  1681  
  1682  			go endpoints.Run(context.TODO(), 1)
  1683  
  1684  			addPods(endpoints.podStore, ns, tc.podsCount, 1, 0, ipv4only)
  1685  
  1686  			endpoints.serviceStore.Add(&v1.Service{
  1687  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1688  				Spec: v1.ServiceSpec{
  1689  					Selector: map[string]string{"foo": "bar"},
  1690  					Ports:    []v1.ServicePort{{Port: 80}},
  1691  				},
  1692  			})
  1693  
  1694  			for _, update := range tc.updates {
  1695  				time.Sleep(update.delay)
  1696  
  1697  				old, exists, err := endpoints.podStore.GetByKey(fmt.Sprintf("%s/%s", ns, update.podName))
  1698  				if err != nil {
  1699  					t.Fatalf("Error while retrieving old value of %q: %v", update.podName, err)
  1700  				}
  1701  				if !exists {
  1702  					t.Fatalf("Pod %q doesn't exist", update.podName)
  1703  				}
  1704  				oldPod := old.(*v1.Pod)
  1705  				newPod := oldPod.DeepCopy()
  1706  				newPod.Status.PodIP = update.podIP
  1707  				newPod.Status.PodIPs[0].IP = update.podIP
  1708  				newPod.ResourceVersion = strconv.Itoa(resourceVersion)
  1709  				resourceVersion++
  1710  
  1711  				endpoints.podStore.Update(newPod)
  1712  				endpoints.updatePod(oldPod, newPod)
  1713  			}
  1714  
  1715  			time.Sleep(tc.finalDelay)
  1716  			endpointsHandler.ValidateRequestCount(t, tc.wantRequestCount)
  1717  		})
  1718  	}
  1719  }
  1720  
  1721  // TestPodAddsBatching verifies that endpoint updates caused by pod addition are batched together.
  1722  // This test uses real time.Sleep, as there is no easy way to mock time in endpoints controller now.
  1723  // TODO(mborsz): Migrate this test to mock clock when possible.
  1724  func TestPodAddsBatching(t *testing.T) {
  1725  	type podAdd struct {
  1726  		delay time.Duration
  1727  	}
  1728  
  1729  	tests := []struct {
  1730  		name             string
  1731  		batchPeriod      time.Duration
  1732  		adds             []podAdd
  1733  		finalDelay       time.Duration
  1734  		wantRequestCount int
  1735  	}{
  1736  		{
  1737  			name:        "three adds with no batching",
  1738  			batchPeriod: 0 * time.Second,
  1739  			adds: []podAdd{
  1740  				{
  1741  					// endpoints.Run needs ~100 ms to start processing updates.
  1742  					delay: 200 * time.Millisecond,
  1743  				},
  1744  				{
  1745  					delay: 100 * time.Millisecond,
  1746  				},
  1747  				{
  1748  					delay: 100 * time.Millisecond,
  1749  				},
  1750  			},
  1751  			finalDelay:       3 * time.Second,
  1752  			wantRequestCount: 3,
  1753  		},
  1754  		{
  1755  			name:        "three adds in one batch",
  1756  			batchPeriod: 1 * time.Second,
  1757  			adds: []podAdd{
  1758  				{
  1759  					// endpoints.Run needs ~100 ms to start processing updates.
  1760  					delay: 200 * time.Millisecond,
  1761  				},
  1762  				{
  1763  					delay: 100 * time.Millisecond,
  1764  				},
  1765  				{
  1766  					delay: 100 * time.Millisecond,
  1767  				},
  1768  			},
  1769  			finalDelay:       3 * time.Second,
  1770  			wantRequestCount: 1,
  1771  		},
  1772  		{
  1773  			name:        "three adds in two batches",
  1774  			batchPeriod: 1 * time.Second,
  1775  			adds: []podAdd{
  1776  				{
  1777  					// endpoints.Run needs ~100 ms to start processing updates.
  1778  					delay: 200 * time.Millisecond,
  1779  				},
  1780  				{
  1781  					delay: 100 * time.Millisecond,
  1782  				},
  1783  				{
  1784  					delay: 1 * time.Second,
  1785  				},
  1786  			},
  1787  			finalDelay:       3 * time.Second,
  1788  			wantRequestCount: 2,
  1789  		},
  1790  	}
  1791  
  1792  	for _, tc := range tests {
  1793  		t.Run(tc.name, func(t *testing.T) {
  1794  			ns := "other"
  1795  			testServer, endpointsHandler := makeTestServer(t, ns)
  1796  			defer testServer.Close()
  1797  			endpoints := newController(testServer.URL, tc.batchPeriod)
  1798  			stopCh := make(chan struct{})
  1799  			defer close(stopCh)
  1800  			endpoints.podsSynced = alwaysReady
  1801  			endpoints.servicesSynced = alwaysReady
  1802  			endpoints.endpointsSynced = alwaysReady
  1803  			endpoints.workerLoopPeriod = 10 * time.Millisecond
  1804  
  1805  			go endpoints.Run(context.TODO(), 1)
  1806  
  1807  			endpoints.serviceStore.Add(&v1.Service{
  1808  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1809  				Spec: v1.ServiceSpec{
  1810  					Selector: map[string]string{"foo": "bar"},
  1811  					Ports:    []v1.ServicePort{{Port: 80}},
  1812  				},
  1813  			})
  1814  
  1815  			for i, add := range tc.adds {
  1816  				time.Sleep(add.delay)
  1817  
  1818  				p := testPod(ns, i, 1, true, ipv4only)
  1819  				endpoints.podStore.Add(p)
  1820  				endpoints.addPod(p)
  1821  			}
  1822  
  1823  			time.Sleep(tc.finalDelay)
  1824  			endpointsHandler.ValidateRequestCount(t, tc.wantRequestCount)
  1825  		})
  1826  	}
  1827  }
  1828  
  1829  // TestPodDeleteBatching verifies that endpoint updates caused by pod deletion are batched together.
  1830  // This test uses real time.Sleep, as there is no easy way to mock time in endpoints controller now.
  1831  // TODO(mborsz): Migrate this test to mock clock when possible.
  1832  func TestPodDeleteBatching(t *testing.T) {
  1833  	type podDelete struct {
  1834  		delay   time.Duration
  1835  		podName string
  1836  	}
  1837  
  1838  	tests := []struct {
  1839  		name             string
  1840  		batchPeriod      time.Duration
  1841  		podsCount        int
  1842  		deletes          []podDelete
  1843  		finalDelay       time.Duration
  1844  		wantRequestCount int
  1845  	}{
  1846  		{
  1847  			name:        "three deletes with no batching",
  1848  			batchPeriod: 0 * time.Second,
  1849  			podsCount:   10,
  1850  			deletes: []podDelete{
  1851  				{
  1852  					// endpoints.Run needs ~100 ms to start processing updates.
  1853  					delay:   200 * time.Millisecond,
  1854  					podName: "pod0",
  1855  				},
  1856  				{
  1857  					delay:   100 * time.Millisecond,
  1858  					podName: "pod1",
  1859  				},
  1860  				{
  1861  					delay:   100 * time.Millisecond,
  1862  					podName: "pod2",
  1863  				},
  1864  			},
  1865  			finalDelay:       3 * time.Second,
  1866  			wantRequestCount: 3,
  1867  		},
  1868  		{
  1869  			name:        "three deletes in one batch",
  1870  			batchPeriod: 1 * time.Second,
  1871  			podsCount:   10,
  1872  			deletes: []podDelete{
  1873  				{
  1874  					// endpoints.Run needs ~100 ms to start processing updates.
  1875  					delay:   200 * time.Millisecond,
  1876  					podName: "pod0",
  1877  				},
  1878  				{
  1879  					delay:   100 * time.Millisecond,
  1880  					podName: "pod1",
  1881  				},
  1882  				{
  1883  					delay:   100 * time.Millisecond,
  1884  					podName: "pod2",
  1885  				},
  1886  			},
  1887  			finalDelay:       3 * time.Second,
  1888  			wantRequestCount: 1,
  1889  		},
  1890  		{
  1891  			name:        "three deletes in two batches",
  1892  			batchPeriod: 1 * time.Second,
  1893  			podsCount:   10,
  1894  			deletes: []podDelete{
  1895  				{
  1896  					// endpoints.Run needs ~100 ms to start processing updates.
  1897  					delay:   200 * time.Millisecond,
  1898  					podName: "pod0",
  1899  				},
  1900  				{
  1901  					delay:   100 * time.Millisecond,
  1902  					podName: "pod1",
  1903  				},
  1904  				{
  1905  					delay:   1 * time.Second,
  1906  					podName: "pod2",
  1907  				},
  1908  			},
  1909  			finalDelay:       3 * time.Second,
  1910  			wantRequestCount: 2,
  1911  		},
  1912  	}
  1913  
  1914  	for _, tc := range tests {
  1915  		t.Run(tc.name, func(t *testing.T) {
  1916  			ns := "other"
  1917  			testServer, endpointsHandler := makeTestServer(t, ns)
  1918  			defer testServer.Close()
  1919  			endpoints := newController(testServer.URL, tc.batchPeriod)
  1920  			stopCh := make(chan struct{})
  1921  			defer close(stopCh)
  1922  			endpoints.podsSynced = alwaysReady
  1923  			endpoints.servicesSynced = alwaysReady
  1924  			endpoints.endpointsSynced = alwaysReady
  1925  			endpoints.workerLoopPeriod = 10 * time.Millisecond
  1926  
  1927  			go endpoints.Run(context.TODO(), 1)
  1928  
  1929  			addPods(endpoints.podStore, ns, tc.podsCount, 1, 0, ipv4only)
  1930  
  1931  			endpoints.serviceStore.Add(&v1.Service{
  1932  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  1933  				Spec: v1.ServiceSpec{
  1934  					Selector: map[string]string{"foo": "bar"},
  1935  					Ports:    []v1.ServicePort{{Port: 80}},
  1936  				},
  1937  			})
  1938  
  1939  			for _, update := range tc.deletes {
  1940  				time.Sleep(update.delay)
  1941  
  1942  				old, exists, err := endpoints.podStore.GetByKey(fmt.Sprintf("%s/%s", ns, update.podName))
  1943  				if err != nil {
  1944  					t.Fatalf("Error while retrieving old value of %q: %v", update.podName, err)
  1945  				}
  1946  				if !exists {
  1947  					t.Fatalf("Pod %q doesn't exist", update.podName)
  1948  				}
  1949  				endpoints.podStore.Delete(old)
  1950  				endpoints.deletePod(old)
  1951  			}
  1952  
  1953  			time.Sleep(tc.finalDelay)
  1954  			endpointsHandler.ValidateRequestCount(t, tc.wantRequestCount)
  1955  		})
  1956  	}
  1957  }
  1958  
  1959  func TestSyncEndpointsServiceNotFound(t *testing.T) {
  1960  	ns := metav1.NamespaceDefault
  1961  	testServer, endpointsHandler := makeTestServer(t, ns)
  1962  	defer testServer.Close()
  1963  	endpoints := newController(testServer.URL, 0)
  1964  	endpoints.endpointsStore.Add(&v1.Endpoints{
  1965  		ObjectMeta: metav1.ObjectMeta{
  1966  			Name:            "foo",
  1967  			Namespace:       ns,
  1968  			ResourceVersion: "1",
  1969  		},
  1970  	})
  1971  	endpoints.syncService(context.TODO(), ns+"/foo")
  1972  	endpointsHandler.ValidateRequestCount(t, 1)
  1973  	endpointsHandler.ValidateRequest(t, "/api/v1/namespaces/"+ns+"/endpoints/foo", "DELETE", nil)
  1974  }
  1975  
  1976  func TestSyncServiceOverCapacity(t *testing.T) {
  1977  	testCases := []struct {
  1978  		name                string
  1979  		startingAnnotation  *string
  1980  		numExisting         int
  1981  		numDesired          int
  1982  		numDesiredNotReady  int
  1983  		numExpectedReady    int
  1984  		numExpectedNotReady int
  1985  		expectedAnnotation  bool
  1986  	}{{
  1987  		name:                "empty",
  1988  		startingAnnotation:  nil,
  1989  		numExisting:         0,
  1990  		numDesired:          0,
  1991  		numExpectedReady:    0,
  1992  		numExpectedNotReady: 0,
  1993  		expectedAnnotation:  false,
  1994  	}, {
  1995  		name:                "annotation added past capacity, < than maxCapacity of Ready Addresses",
  1996  		startingAnnotation:  nil,
  1997  		numExisting:         maxCapacity - 1,
  1998  		numDesired:          maxCapacity - 3,
  1999  		numDesiredNotReady:  4,
  2000  		numExpectedReady:    maxCapacity - 3,
  2001  		numExpectedNotReady: 3,
  2002  		expectedAnnotation:  true,
  2003  	}, {
  2004  		name:                "annotation added past capacity, maxCapacity of Ready Addresses ",
  2005  		startingAnnotation:  nil,
  2006  		numExisting:         maxCapacity - 1,
  2007  		numDesired:          maxCapacity,
  2008  		numDesiredNotReady:  10,
  2009  		numExpectedReady:    maxCapacity,
  2010  		numExpectedNotReady: 0,
  2011  		expectedAnnotation:  true,
  2012  	}, {
  2013  		name:                "annotation removed below capacity",
  2014  		startingAnnotation:  pointer.String("truncated"),
  2015  		numExisting:         maxCapacity - 1,
  2016  		numDesired:          maxCapacity - 1,
  2017  		numDesiredNotReady:  0,
  2018  		numExpectedReady:    maxCapacity - 1,
  2019  		numExpectedNotReady: 0,
  2020  		expectedAnnotation:  false,
  2021  	}, {
  2022  		name:                "annotation was set to warning previously, annotation removed at capacity",
  2023  		startingAnnotation:  pointer.String("warning"),
  2024  		numExisting:         maxCapacity,
  2025  		numDesired:          maxCapacity,
  2026  		numDesiredNotReady:  0,
  2027  		numExpectedReady:    maxCapacity,
  2028  		numExpectedNotReady: 0,
  2029  		expectedAnnotation:  false,
  2030  	}, {
  2031  		name:                "annotation was set to warning previously but still over capacity",
  2032  		startingAnnotation:  pointer.String("warning"),
  2033  		numExisting:         maxCapacity + 1,
  2034  		numDesired:          maxCapacity + 1,
  2035  		numDesiredNotReady:  0,
  2036  		numExpectedReady:    maxCapacity,
  2037  		numExpectedNotReady: 0,
  2038  		expectedAnnotation:  true,
  2039  	}, {
  2040  		name:                "annotation removed at capacity",
  2041  		startingAnnotation:  pointer.String("truncated"),
  2042  		numExisting:         maxCapacity,
  2043  		numDesired:          maxCapacity,
  2044  		numDesiredNotReady:  0,
  2045  		numExpectedReady:    maxCapacity,
  2046  		numExpectedNotReady: 0,
  2047  		expectedAnnotation:  false,
  2048  	}, {
  2049  		name:                "no endpoints change, annotation value corrected",
  2050  		startingAnnotation:  pointer.String("invalid"),
  2051  		numExisting:         maxCapacity + 1,
  2052  		numDesired:          maxCapacity + 1,
  2053  		numDesiredNotReady:  0,
  2054  		numExpectedReady:    maxCapacity,
  2055  		numExpectedNotReady: 0,
  2056  		expectedAnnotation:  true,
  2057  	}}
  2058  
  2059  	for _, tc := range testCases {
  2060  		t.Run(tc.name, func(t *testing.T) {
  2061  			ns := "test"
  2062  			client, c := newFakeController(0 * time.Second)
  2063  
  2064  			addPods(c.podStore, ns, tc.numDesired, 1, tc.numDesiredNotReady, ipv4only)
  2065  			pods := c.podStore.List()
  2066  
  2067  			svc := &v1.Service{
  2068  				ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  2069  				Spec: v1.ServiceSpec{
  2070  					Selector: map[string]string{"foo": "bar"},
  2071  					Ports:    []v1.ServicePort{{Port: 80}},
  2072  				},
  2073  			}
  2074  			c.serviceStore.Add(svc)
  2075  
  2076  			subset := v1.EndpointSubset{}
  2077  			for i := 0; i < tc.numExisting; i++ {
  2078  				pod := pods[i].(*v1.Pod)
  2079  				epa, _ := podToEndpointAddressForService(svc, pod)
  2080  				subset.Addresses = append(subset.Addresses, *epa)
  2081  			}
  2082  			endpoints := &v1.Endpoints{
  2083  				ObjectMeta: metav1.ObjectMeta{
  2084  					Name:            svc.Name,
  2085  					Namespace:       ns,
  2086  					ResourceVersion: "1",
  2087  					Annotations:     map[string]string{},
  2088  				},
  2089  				Subsets: []v1.EndpointSubset{subset},
  2090  			}
  2091  			if tc.startingAnnotation != nil {
  2092  				endpoints.Annotations[v1.EndpointsOverCapacity] = *tc.startingAnnotation
  2093  			}
  2094  			c.endpointsStore.Add(endpoints)
  2095  			client.CoreV1().Endpoints(ns).Create(context.TODO(), endpoints, metav1.CreateOptions{})
  2096  
  2097  			c.syncService(context.TODO(), fmt.Sprintf("%s/%s", ns, svc.Name))
  2098  
  2099  			actualEndpoints, err := client.CoreV1().Endpoints(ns).Get(context.TODO(), endpoints.Name, metav1.GetOptions{})
  2100  			if err != nil {
  2101  				t.Fatalf("unexpected error getting endpoints: %v", err)
  2102  			}
  2103  
  2104  			actualAnnotation, ok := actualEndpoints.Annotations[v1.EndpointsOverCapacity]
  2105  			if tc.expectedAnnotation {
  2106  				if !ok {
  2107  					t.Errorf("Expected EndpointsOverCapacity annotation to be set")
  2108  				} else if actualAnnotation != "truncated" {
  2109  					t.Errorf("Expected EndpointsOverCapacity annotation to be 'truncated', got %s", actualAnnotation)
  2110  				}
  2111  			} else {
  2112  				if ok {
  2113  					t.Errorf("Expected EndpointsOverCapacity annotation not to be set, got %s", actualAnnotation)
  2114  				}
  2115  			}
  2116  			numActualReady := 0
  2117  			numActualNotReady := 0
  2118  			for _, subset := range actualEndpoints.Subsets {
  2119  				numActualReady += len(subset.Addresses)
  2120  				numActualNotReady += len(subset.NotReadyAddresses)
  2121  			}
  2122  			if numActualReady != tc.numExpectedReady {
  2123  				t.Errorf("Unexpected number of actual ready Endpoints: got %d endpoints, want %d endpoints", numActualReady, tc.numExpectedReady)
  2124  			}
  2125  			if numActualNotReady != tc.numExpectedNotReady {
  2126  				t.Errorf("Unexpected number of actual not ready Endpoints: got %d endpoints, want %d endpoints", numActualNotReady, tc.numExpectedNotReady)
  2127  			}
  2128  		})
  2129  	}
  2130  }
  2131  
  2132  func TestTruncateEndpoints(t *testing.T) {
  2133  	testCases := []struct {
  2134  		desc string
  2135  		// subsetsReady, subsetsNotReady, expectedReady, expectedNotReady
  2136  		// must all be the same length
  2137  		subsetsReady     []int
  2138  		subsetsNotReady  []int
  2139  		expectedReady    []int
  2140  		expectedNotReady []int
  2141  	}{{
  2142  		desc:             "empty",
  2143  		subsetsReady:     []int{},
  2144  		subsetsNotReady:  []int{},
  2145  		expectedReady:    []int{},
  2146  		expectedNotReady: []int{},
  2147  	}, {
  2148  		desc:             "total endpoints < max capacity",
  2149  		subsetsReady:     []int{50, 100, 100, 100, 100},
  2150  		subsetsNotReady:  []int{50, 100, 100, 100, 100},
  2151  		expectedReady:    []int{50, 100, 100, 100, 100},
  2152  		expectedNotReady: []int{50, 100, 100, 100, 100},
  2153  	}, {
  2154  		desc:             "total endpoints = max capacity",
  2155  		subsetsReady:     []int{100, 100, 100, 100, 100},
  2156  		subsetsNotReady:  []int{100, 100, 100, 100, 100},
  2157  		expectedReady:    []int{100, 100, 100, 100, 100},
  2158  		expectedNotReady: []int{100, 100, 100, 100, 100},
  2159  	}, {
  2160  		desc:             "total ready endpoints < max capacity, but total endpoints > max capacity",
  2161  		subsetsReady:     []int{90, 110, 50, 10, 20},
  2162  		subsetsNotReady:  []int{101, 200, 200, 201, 298},
  2163  		expectedReady:    []int{90, 110, 50, 10, 20},
  2164  		expectedNotReady: []int{73, 144, 144, 145, 214},
  2165  	}, {
  2166  		desc:             "total ready endpoints > max capacity",
  2167  		subsetsReady:     []int{205, 400, 402, 400, 693},
  2168  		subsetsNotReady:  []int{100, 200, 200, 200, 300},
  2169  		expectedReady:    []int{98, 191, 192, 191, 328},
  2170  		expectedNotReady: []int{0, 0, 0, 0, 0},
  2171  	}}
  2172  
  2173  	for _, tc := range testCases {
  2174  		t.Run(tc.desc, func(t *testing.T) {
  2175  			var subsets []v1.EndpointSubset
  2176  			for subsetIndex, numReady := range tc.subsetsReady {
  2177  				subset := v1.EndpointSubset{}
  2178  				for i := 0; i < numReady; i++ {
  2179  					subset.Addresses = append(subset.Addresses, v1.EndpointAddress{})
  2180  				}
  2181  
  2182  				numNotReady := tc.subsetsNotReady[subsetIndex]
  2183  				for i := 0; i < numNotReady; i++ {
  2184  					subset.NotReadyAddresses = append(subset.NotReadyAddresses, v1.EndpointAddress{})
  2185  				}
  2186  				subsets = append(subsets, subset)
  2187  			}
  2188  
  2189  			endpoints := &v1.Endpoints{Subsets: subsets}
  2190  			truncateEndpoints(endpoints)
  2191  
  2192  			for i, subset := range endpoints.Subsets {
  2193  				if len(subset.Addresses) != tc.expectedReady[i] {
  2194  					t.Errorf("Unexpected number of actual ready Endpoints for subset %d: got %d endpoints, want %d endpoints", i, len(subset.Addresses), tc.expectedReady[i])
  2195  				}
  2196  				if len(subset.NotReadyAddresses) != tc.expectedNotReady[i] {
  2197  					t.Errorf("Unexpected number of actual not ready Endpoints for subset %d: got %d endpoints, want %d endpoints", i, len(subset.NotReadyAddresses), tc.expectedNotReady[i])
  2198  				}
  2199  			}
  2200  		})
  2201  	}
  2202  }
  2203  
  2204  func TestEndpointPortFromServicePort(t *testing.T) {
  2205  	http := pointer.String("http")
  2206  	testCases := map[string]struct {
  2207  		serviceAppProtocol           *string
  2208  		expectedEndpointsAppProtocol *string
  2209  	}{
  2210  		"empty app protocol": {
  2211  			serviceAppProtocol:           nil,
  2212  			expectedEndpointsAppProtocol: nil,
  2213  		},
  2214  		"http app protocol": {
  2215  			serviceAppProtocol:           http,
  2216  			expectedEndpointsAppProtocol: http,
  2217  		},
  2218  	}
  2219  
  2220  	for name, tc := range testCases {
  2221  		t.Run(name, func(t *testing.T) {
  2222  			epp := endpointPortFromServicePort(&v1.ServicePort{Name: "test", AppProtocol: tc.serviceAppProtocol}, 80)
  2223  
  2224  			if epp.AppProtocol != tc.expectedEndpointsAppProtocol {
  2225  				t.Errorf("Expected Endpoints AppProtocol to be %s, got %s", stringVal(tc.expectedEndpointsAppProtocol), stringVal(epp.AppProtocol))
  2226  			}
  2227  		})
  2228  	}
  2229  }
  2230  
  2231  // TestMultipleServiceChanges tests that endpoints that are not created because of an out of sync endpoints cache are eventually recreated
  2232  // A service will be created. After the endpoints exist, the service will be deleted and the endpoints will not be deleted from the cache immediately.
  2233  // After the service is recreated, the endpoints will be deleted replicating an out of sync cache. Expect that eventually the endpoints will be recreated.
  2234  func TestMultipleServiceChanges(t *testing.T) {
  2235  	ns := metav1.NamespaceDefault
  2236  	expectedSubsets := []v1.EndpointSubset{{
  2237  		Addresses: []v1.EndpointAddress{
  2238  			{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}},
  2239  		},
  2240  	}}
  2241  	endpoint := &v1.Endpoints{
  2242  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns, ResourceVersion: "1"},
  2243  		Subsets:    expectedSubsets,
  2244  	}
  2245  
  2246  	controller := &endpointController{}
  2247  	blockDelete := make(chan struct{})
  2248  	blockNextAction := make(chan struct{})
  2249  	stopChan := make(chan struct{})
  2250  	testServer := makeBlockingEndpointDeleteTestServer(t, controller, endpoint, blockDelete, blockNextAction, ns)
  2251  	defer testServer.Close()
  2252  
  2253  	*controller = *newController(testServer.URL, 0*time.Second)
  2254  	addPods(controller.podStore, ns, 1, 1, 0, ipv4only)
  2255  
  2256  	go func() { controller.Run(context.TODO(), 1) }()
  2257  
  2258  	svc := &v1.Service{
  2259  		ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
  2260  		Spec: v1.ServiceSpec{
  2261  			Selector:  map[string]string{"foo": "bar"},
  2262  			ClusterIP: "None",
  2263  			Ports:     nil,
  2264  		},
  2265  	}
  2266  
  2267  	controller.serviceStore.Add(svc)
  2268  	controller.onServiceUpdate(svc)
  2269  	// blockNextAction should eventually unblock once server gets endpoint request.
  2270  	waitForChanReceive(t, 1*time.Second, blockNextAction, "Service Add should have caused a request to be sent to the test server")
  2271  
  2272  	controller.serviceStore.Delete(svc)
  2273  	controller.onServiceDelete(svc)
  2274  	waitForChanReceive(t, 1*time.Second, blockNextAction, "Service Delete should have caused a request to be sent to the test server")
  2275  
  2276  	// If endpoints cache has not updated before service update is registered
  2277  	// Services add will not trigger a Create endpoint request.
  2278  	controller.serviceStore.Add(svc)
  2279  	controller.onServiceUpdate(svc)
  2280  
  2281  	// Ensure the work queue has been processed by looping for up to a second to prevent flakes.
  2282  	wait.PollImmediate(50*time.Millisecond, 1*time.Second, func() (bool, error) {
  2283  		return controller.queue.Len() == 0, nil
  2284  	})
  2285  
  2286  	// Cause test server to delete endpoints
  2287  	close(blockDelete)
  2288  	waitForChanReceive(t, 1*time.Second, blockNextAction, "Endpoint should have been recreated")
  2289  
  2290  	close(blockNextAction)
  2291  	close(stopChan)
  2292  }
  2293  
  2294  func TestSyncServiceAddresses(t *testing.T) {
  2295  	makeService := func(tolerateUnready bool) *v1.Service {
  2296  		return &v1.Service{
  2297  			ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "ns"},
  2298  			Spec: v1.ServiceSpec{
  2299  				Selector:                 map[string]string{"foo": "bar"},
  2300  				PublishNotReadyAddresses: tolerateUnready,
  2301  				Type:                     v1.ServiceTypeClusterIP,
  2302  				ClusterIP:                "1.1.1.1",
  2303  				Ports:                    []v1.ServicePort{{Port: 80}},
  2304  			},
  2305  		}
  2306  	}
  2307  
  2308  	makePod := func(phase v1.PodPhase, isReady bool, terminating bool) *v1.Pod {
  2309  		statusCondition := v1.ConditionFalse
  2310  		if isReady {
  2311  			statusCondition = v1.ConditionTrue
  2312  		}
  2313  
  2314  		now := metav1.Now()
  2315  		deletionTimestamp := &now
  2316  		if !terminating {
  2317  			deletionTimestamp = nil
  2318  		}
  2319  		return &v1.Pod{
  2320  			ObjectMeta: metav1.ObjectMeta{
  2321  				Namespace:         "ns",
  2322  				Name:              "fakepod",
  2323  				DeletionTimestamp: deletionTimestamp,
  2324  				Labels:            map[string]string{"foo": "bar"},
  2325  			},
  2326  			Spec: v1.PodSpec{
  2327  				Containers: []v1.Container{{Ports: []v1.ContainerPort{
  2328  					{Name: "port1", ContainerPort: int32(8080)},
  2329  				}}},
  2330  			},
  2331  			Status: v1.PodStatus{
  2332  				Phase: phase,
  2333  				Conditions: []v1.PodCondition{
  2334  					{
  2335  						Type:   v1.PodReady,
  2336  						Status: statusCondition,
  2337  					},
  2338  				},
  2339  				PodIP: "10.1.1.1",
  2340  				PodIPs: []v1.PodIP{
  2341  					{IP: "10.1.1.1"},
  2342  				},
  2343  			},
  2344  		}
  2345  	}
  2346  
  2347  	testCases := []struct {
  2348  		name            string
  2349  		pod             *v1.Pod
  2350  		service         *v1.Service
  2351  		expectedReady   int
  2352  		expectedUnready int
  2353  	}{
  2354  		{
  2355  			name:            "pod running phase",
  2356  			pod:             makePod(v1.PodRunning, true, false),
  2357  			service:         makeService(false),
  2358  			expectedReady:   1,
  2359  			expectedUnready: 0,
  2360  		},
  2361  		{
  2362  			name:            "pod running phase being deleted",
  2363  			pod:             makePod(v1.PodRunning, true, true),
  2364  			service:         makeService(false),
  2365  			expectedReady:   0,
  2366  			expectedUnready: 0,
  2367  		},
  2368  		{
  2369  			name:            "pod unknown phase container ready",
  2370  			pod:             makePod(v1.PodUnknown, true, false),
  2371  			service:         makeService(false),
  2372  			expectedReady:   1,
  2373  			expectedUnready: 0,
  2374  		},
  2375  		{
  2376  			name:            "pod unknown phase container ready being deleted",
  2377  			pod:             makePod(v1.PodUnknown, true, true),
  2378  			service:         makeService(false),
  2379  			expectedReady:   0,
  2380  			expectedUnready: 0,
  2381  		},
  2382  		{
  2383  			name:            "pod pending phase container ready",
  2384  			pod:             makePod(v1.PodPending, true, false),
  2385  			service:         makeService(false),
  2386  			expectedReady:   1,
  2387  			expectedUnready: 0,
  2388  		},
  2389  		{
  2390  			name:            "pod pending phase container ready being deleted",
  2391  			pod:             makePod(v1.PodPending, true, true),
  2392  			service:         makeService(false),
  2393  			expectedReady:   0,
  2394  			expectedUnready: 0,
  2395  		},
  2396  		{
  2397  			name:            "pod unknown phase container not ready",
  2398  			pod:             makePod(v1.PodUnknown, false, false),
  2399  			service:         makeService(false),
  2400  			expectedReady:   0,
  2401  			expectedUnready: 1,
  2402  		},
  2403  		{
  2404  			name:            "pod pending phase container not ready",
  2405  			pod:             makePod(v1.PodPending, false, false),
  2406  			service:         makeService(false),
  2407  			expectedReady:   0,
  2408  			expectedUnready: 1,
  2409  		},
  2410  		{
  2411  			name:            "pod failed phase",
  2412  			pod:             makePod(v1.PodFailed, false, false),
  2413  			service:         makeService(false),
  2414  			expectedReady:   0,
  2415  			expectedUnready: 0,
  2416  		},
  2417  		{
  2418  			name:            "pod succeeded phase",
  2419  			pod:             makePod(v1.PodSucceeded, false, false),
  2420  			service:         makeService(false),
  2421  			expectedReady:   0,
  2422  			expectedUnready: 0,
  2423  		},
  2424  		{
  2425  			name:            "pod running phase and tolerate unready",
  2426  			pod:             makePod(v1.PodRunning, false, false),
  2427  			service:         makeService(true),
  2428  			expectedReady:   1,
  2429  			expectedUnready: 0,
  2430  		},
  2431  		{
  2432  			name:            "pod running phase and tolerate unready being deleted",
  2433  			pod:             makePod(v1.PodRunning, false, true),
  2434  			service:         makeService(true),
  2435  			expectedReady:   1,
  2436  			expectedUnready: 0,
  2437  		},
  2438  		{
  2439  			name:            "pod unknown phase and tolerate unready",
  2440  			pod:             makePod(v1.PodUnknown, false, false),
  2441  			service:         makeService(true),
  2442  			expectedReady:   1,
  2443  			expectedUnready: 0,
  2444  		},
  2445  		{
  2446  			name:            "pod unknown phase and tolerate unready being deleted",
  2447  			pod:             makePod(v1.PodUnknown, false, true),
  2448  			service:         makeService(true),
  2449  			expectedReady:   1,
  2450  			expectedUnready: 0,
  2451  		},
  2452  		{
  2453  			name:            "pod pending phase and tolerate unready",
  2454  			pod:             makePod(v1.PodPending, false, false),
  2455  			service:         makeService(true),
  2456  			expectedReady:   1,
  2457  			expectedUnready: 0,
  2458  		},
  2459  		{
  2460  			name:            "pod pending phase and tolerate unready being deleted",
  2461  			pod:             makePod(v1.PodPending, false, true),
  2462  			service:         makeService(true),
  2463  			expectedReady:   1,
  2464  			expectedUnready: 0,
  2465  		},
  2466  		{
  2467  			name:            "pod failed phase and tolerate unready",
  2468  			pod:             makePod(v1.PodFailed, false, false),
  2469  			service:         makeService(true),
  2470  			expectedReady:   0,
  2471  			expectedUnready: 0,
  2472  		},
  2473  		{
  2474  			name:            "pod succeeded phase and tolerate unready endpoints",
  2475  			pod:             makePod(v1.PodSucceeded, false, false),
  2476  			service:         makeService(true),
  2477  			expectedReady:   0,
  2478  			expectedUnready: 0,
  2479  		},
  2480  	}
  2481  
  2482  	for _, tc := range testCases {
  2483  		t.Run(tc.name, func(t *testing.T) {
  2484  			ns := tc.service.Namespace
  2485  			client, c := newFakeController(0 * time.Second)
  2486  
  2487  			err := c.podStore.Add(tc.pod)
  2488  			if err != nil {
  2489  				t.Errorf("Unexpected error adding pod %v", err)
  2490  			}
  2491  			err = c.serviceStore.Add(tc.service)
  2492  			if err != nil {
  2493  				t.Errorf("Unexpected error adding service %v", err)
  2494  			}
  2495  			err = c.syncService(context.TODO(), fmt.Sprintf("%s/%s", ns, tc.service.Name))
  2496  			if err != nil {
  2497  				t.Errorf("Unexpected error syncing service %v", err)
  2498  			}
  2499  
  2500  			endpoints, err := client.CoreV1().Endpoints(ns).Get(context.TODO(), tc.service.Name, metav1.GetOptions{})
  2501  			if err != nil {
  2502  				t.Errorf("Unexpected error %v", err)
  2503  			}
  2504  
  2505  			readyEndpoints := 0
  2506  			unreadyEndpoints := 0
  2507  			for _, subset := range endpoints.Subsets {
  2508  				readyEndpoints += len(subset.Addresses)
  2509  				unreadyEndpoints += len(subset.NotReadyAddresses)
  2510  			}
  2511  
  2512  			if tc.expectedReady != readyEndpoints {
  2513  				t.Errorf("Expected %d ready endpoints, got %d", tc.expectedReady, readyEndpoints)
  2514  			}
  2515  
  2516  			if tc.expectedUnready != unreadyEndpoints {
  2517  				t.Errorf("Expected %d ready endpoints, got %d", tc.expectedUnready, unreadyEndpoints)
  2518  			}
  2519  		})
  2520  	}
  2521  }
  2522  
  2523  func TestEndpointsDeletionEvents(t *testing.T) {
  2524  	ns := metav1.NamespaceDefault
  2525  	testServer, _ := makeTestServer(t, ns)
  2526  	defer testServer.Close()
  2527  	controller := newController(testServer.URL, 0)
  2528  	store := controller.endpointsStore
  2529  	ep1 := &v1.Endpoints{
  2530  		ObjectMeta: metav1.ObjectMeta{
  2531  			Name:            "foo",
  2532  			Namespace:       ns,
  2533  			ResourceVersion: "rv1",
  2534  		},
  2535  	}
  2536  
  2537  	// Test Unexpected and Expected Deletes
  2538  	store.Delete(ep1)
  2539  	controller.onEndpointsDelete(ep1)
  2540  
  2541  	if controller.queue.Len() != 1 {
  2542  		t.Errorf("Expected one service to be in the queue, found %d", controller.queue.Len())
  2543  	}
  2544  }
  2545  
  2546  func stringVal(str *string) string {
  2547  	if str == nil {
  2548  		return "nil"
  2549  	}
  2550  	return *str
  2551  }
  2552  
  2553  // waitForChanReceive blocks up to the timeout waiting for the receivingChan to receive
  2554  func waitForChanReceive(t *testing.T, timeout time.Duration, receivingChan chan struct{}, errorMsg string) {
  2555  	timer := time.NewTimer(timeout)
  2556  	select {
  2557  	case <-timer.C:
  2558  		t.Errorf(errorMsg)
  2559  	case <-receivingChan:
  2560  	}
  2561  }
  2562  
  2563  func TestEndpointSubsetsEqualIgnoreResourceVersion(t *testing.T) {
  2564  	copyAndMutateEndpointSubset := func(orig *v1.EndpointSubset, mutator func(*v1.EndpointSubset)) *v1.EndpointSubset {
  2565  		newSubSet := orig.DeepCopy()
  2566  		mutator(newSubSet)
  2567  		return newSubSet
  2568  	}
  2569  	es1 := &v1.EndpointSubset{
  2570  		Addresses: []v1.EndpointAddress{
  2571  			{
  2572  				IP:        "1.1.1.1",
  2573  				TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod1-1", Namespace: "ns", ResourceVersion: "1"},
  2574  			},
  2575  		},
  2576  		NotReadyAddresses: []v1.EndpointAddress{
  2577  			{
  2578  				IP:        "1.1.1.2",
  2579  				TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod1-2", Namespace: "ns2", ResourceVersion: "2"},
  2580  			},
  2581  		},
  2582  		Ports: []v1.EndpointPort{{Port: 8081, Protocol: "TCP"}},
  2583  	}
  2584  	es2 := &v1.EndpointSubset{
  2585  		Addresses: []v1.EndpointAddress{
  2586  			{
  2587  				IP:        "2.2.2.1",
  2588  				TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod2-1", Namespace: "ns", ResourceVersion: "3"},
  2589  			},
  2590  		},
  2591  		NotReadyAddresses: []v1.EndpointAddress{
  2592  			{
  2593  				IP:        "2.2.2.2",
  2594  				TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod2-2", Namespace: "ns2", ResourceVersion: "4"},
  2595  			},
  2596  		},
  2597  		Ports: []v1.EndpointPort{{Port: 8082, Protocol: "TCP"}},
  2598  	}
  2599  	tests := []struct {
  2600  		name     string
  2601  		subsets1 []v1.EndpointSubset
  2602  		subsets2 []v1.EndpointSubset
  2603  		expected bool
  2604  	}{
  2605  		{
  2606  			name:     "Subsets removed",
  2607  			subsets1: []v1.EndpointSubset{*es1, *es2},
  2608  			subsets2: []v1.EndpointSubset{*es1},
  2609  			expected: false,
  2610  		},
  2611  		{
  2612  			name:     "Ready Pod IP changed",
  2613  			subsets1: []v1.EndpointSubset{*es1, *es2},
  2614  			subsets2: []v1.EndpointSubset{*copyAndMutateEndpointSubset(es1, func(es *v1.EndpointSubset) {
  2615  				es.Addresses[0].IP = "1.1.1.10"
  2616  			}), *es2},
  2617  			expected: false,
  2618  		},
  2619  		{
  2620  			name:     "NotReady Pod IP changed",
  2621  			subsets1: []v1.EndpointSubset{*es1, *es2},
  2622  			subsets2: []v1.EndpointSubset{*es1, *copyAndMutateEndpointSubset(es2, func(es *v1.EndpointSubset) {
  2623  				es.NotReadyAddresses[0].IP = "2.2.2.10"
  2624  			})},
  2625  			expected: false,
  2626  		},
  2627  		{
  2628  			name:     "Pod ResourceVersion changed",
  2629  			subsets1: []v1.EndpointSubset{*es1, *es2},
  2630  			subsets2: []v1.EndpointSubset{*es1, *copyAndMutateEndpointSubset(es2, func(es *v1.EndpointSubset) {
  2631  				es.Addresses[0].TargetRef.ResourceVersion = "100"
  2632  			})},
  2633  			expected: true,
  2634  		},
  2635  		{
  2636  			name:     "Pod ResourceVersion removed",
  2637  			subsets1: []v1.EndpointSubset{*es1, *es2},
  2638  			subsets2: []v1.EndpointSubset{*es1, *copyAndMutateEndpointSubset(es2, func(es *v1.EndpointSubset) {
  2639  				es.Addresses[0].TargetRef.ResourceVersion = ""
  2640  			})},
  2641  			expected: true,
  2642  		},
  2643  		{
  2644  			name:     "Ports changed",
  2645  			subsets1: []v1.EndpointSubset{*es1, *es2},
  2646  			subsets2: []v1.EndpointSubset{*es1, *copyAndMutateEndpointSubset(es1, func(es *v1.EndpointSubset) {
  2647  				es.Ports[0].Port = 8082
  2648  			})},
  2649  			expected: false,
  2650  		},
  2651  	}
  2652  	for _, tt := range tests {
  2653  		t.Run(tt.name, func(t *testing.T) {
  2654  			if got := endpointSubsetsEqualIgnoreResourceVersion(tt.subsets1, tt.subsets2); got != tt.expected {
  2655  				t.Errorf("semanticIgnoreResourceVersion.DeepEqual() = %v, expected %v", got, tt.expected)
  2656  			}
  2657  		})
  2658  	}
  2659  }