@@ -160,10 +160,10 @@ func makeTestServer(t *testing.T, namespace string) (*httptest.Server, *utiltest
160
160
return httptest .NewServer (mux ), & fakeEndpointsHandler
161
161
}
162
162
163
- // makeBlockingEndpointDeleteTestServer will signal the blockNextAction channel on endpoint "POST" & "DELETE" requests. All
164
- // block endpoint "DELETE" requestsi will wait on a blockDelete signal to delete endpoint. If controller is nil, a error will
165
- // be sent in the response.
166
- func makeBlockingEndpointDeleteTestServer (t * testing.T , controller * endpointController , endpoint * v1.Endpoints , blockDelete , blockNextAction chan struct {}, namespace string ) * httptest.Server {
163
+ // makeBlockingEndpointTestServer will signal the blockNextAction channel on endpoint "POST", "PUT", and "DELETE"
164
+ // requests. "POST" and "PUT" requests will wait on a blockUpdate signal if provided, while "DELETE" requests will wait
165
+ // on a blockDelete signal if provided. If controller is nil, an error will be sent in the response.
166
+ func makeBlockingEndpointTestServer (t * testing.T , controller * endpointController , endpoint * v1.Endpoints , blockUpdate , blockDelete , blockNextAction chan struct {}, namespace string ) * httptest.Server {
167
167
168
168
handlerFunc := func (res http.ResponseWriter , req * http.Request ) {
169
169
if controller == nil {
@@ -172,23 +172,37 @@ func makeBlockingEndpointDeleteTestServer(t *testing.T, controller *endpointCont
172
172
return
173
173
}
174
174
175
- if req .Method == "POST" {
176
- controller .endpointsStore .Add (endpoint )
175
+ if req .Method == "POST" || req .Method == "PUT" {
176
+ if blockUpdate != nil {
177
+ go func () {
178
+ // Delay the update of endpoints to make endpoints cache out of sync
179
+ <- blockUpdate
180
+ _ = controller .endpointsStore .Add (endpoint )
181
+ }()
182
+ } else {
183
+ _ = controller .endpointsStore .Add (endpoint )
184
+ }
177
185
blockNextAction <- struct {}{}
178
186
}
179
187
180
188
if req .Method == "DELETE" {
181
- go func () {
182
- // Delay the deletion of endoints to make endpoint cache out of sync
183
- <- blockDelete
184
- controller .endpointsStore .Delete (endpoint )
189
+ if blockDelete != nil {
190
+ go func () {
191
+ // Delay the deletion of endpoints to make endpoints cache out of sync
192
+ <- blockDelete
193
+ _ = controller .endpointsStore .Delete (endpoint )
194
+ controller .onEndpointsDelete (endpoint )
195
+ }()
196
+ } else {
197
+ _ = controller .endpointsStore .Delete (endpoint )
185
198
controller .onEndpointsDelete (endpoint )
186
- }()
199
+ }
187
200
blockNextAction <- struct {}{}
188
201
}
189
202
203
+ res .Header ().Set ("Content-Type" , "application/json" )
190
204
res .WriteHeader (http .StatusOK )
191
- res .Write ([]byte (runtime .EncodeOrDie (clientscheme .Codecs .LegacyCodec (v1 .SchemeGroupVersion ), & v1. Endpoints {} )))
205
+ _ , _ = res .Write ([]byte (runtime .EncodeOrDie (clientscheme .Codecs .LegacyCodec (v1 .SchemeGroupVersion ), endpoint )))
192
206
}
193
207
194
208
mux := http .NewServeMux ()
@@ -2378,7 +2392,7 @@ func TestMultipleServiceChanges(t *testing.T) {
2378
2392
blockDelete := make (chan struct {})
2379
2393
blockNextAction := make (chan struct {})
2380
2394
stopChan := make (chan struct {})
2381
- testServer := makeBlockingEndpointDeleteTestServer (t , controller , endpoint , blockDelete , blockNextAction , ns )
2395
+ testServer := makeBlockingEndpointTestServer (t , controller , endpoint , nil , blockDelete , blockNextAction , ns )
2382
2396
defer testServer .Close ()
2383
2397
2384
2398
tCtx := ktesting .Init (t )
@@ -2423,6 +2437,83 @@ func TestMultipleServiceChanges(t *testing.T) {
2423
2437
close (stopChan )
2424
2438
}
2425
2439
2440
+ // TestMultiplePodChanges tests that endpoints that are not updated because of an out of sync endpoints cache are
2441
+ // eventually resynced after multiple Pod changes.
2442
+ func TestMultiplePodChanges (t * testing.T ) {
2443
+ ns := metav1 .NamespaceDefault
2444
+
2445
+ readyEndpoints := & v1.Endpoints {
2446
+ ObjectMeta : metav1.ObjectMeta {Name : "foo" , Namespace : ns , ResourceVersion : "1" },
2447
+ Subsets : []v1.EndpointSubset {{
2448
+ Addresses : []v1.EndpointAddress {
2449
+ {IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1.ObjectReference {Kind : "Pod" , Name : "pod0" , Namespace : ns }},
2450
+ },
2451
+ Ports : []v1.EndpointPort {{Port : 8080 , Protocol : v1 .ProtocolTCP }},
2452
+ }},
2453
+ }
2454
+ notReadyEndpoints := & v1.Endpoints {
2455
+ ObjectMeta : metav1.ObjectMeta {Name : "foo" , Namespace : ns , ResourceVersion : "2" },
2456
+ Subsets : []v1.EndpointSubset {{
2457
+ NotReadyAddresses : []v1.EndpointAddress {
2458
+ {IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1.ObjectReference {Kind : "Pod" , Name : "pod0" , Namespace : ns }},
2459
+ },
2460
+ Ports : []v1.EndpointPort {{Port : 8080 , Protocol : v1 .ProtocolTCP }},
2461
+ }},
2462
+ }
2463
+
2464
+ controller := & endpointController {}
2465
+ blockUpdate := make (chan struct {})
2466
+ blockNextAction := make (chan struct {})
2467
+ stopChan := make (chan struct {})
2468
+ testServer := makeBlockingEndpointTestServer (t , controller , notReadyEndpoints , blockUpdate , nil , blockNextAction , ns )
2469
+ defer testServer .Close ()
2470
+
2471
+ tCtx := ktesting .Init (t )
2472
+ * controller = * newController (tCtx , testServer .URL , 0 * time .Second )
2473
+ pod := testPod (ns , 0 , 1 , true , ipv4only )
2474
+ _ = controller .podStore .Add (pod )
2475
+ _ = controller .endpointsStore .Add (readyEndpoints )
2476
+ _ = controller .serviceStore .Add (& v1.Service {
2477
+ ObjectMeta : metav1.ObjectMeta {Name : "foo" , Namespace : ns },
2478
+ Spec : v1.ServiceSpec {
2479
+ Selector : map [string ]string {"foo" : "bar" },
2480
+ ClusterIP : "10.0.0.1" ,
2481
+ Ports : []v1.ServicePort {{Port : 80 , Protocol : "TCP" , TargetPort : intstr .FromInt32 (8080 )}},
2482
+ },
2483
+ })
2484
+
2485
+ go func () { controller .Run (tCtx , 1 ) }()
2486
+
2487
+ // Rapidly update the Pod: Ready -> NotReady -> Ready.
2488
+ pod2 := pod .DeepCopy ()
2489
+ pod2 .ResourceVersion = "2"
2490
+ pod2 .Status .Conditions [0 ].Status = v1 .ConditionFalse
2491
+ _ = controller .podStore .Update (pod2 )
2492
+ controller .updatePod (pod , pod2 )
2493
+ // blockNextAction should eventually unblock once server gets endpoints request.
2494
+ waitForChanReceive (t , 1 * time .Second , blockNextAction , "Pod Update should have caused a request to be sent to the test server" )
2495
+ // The endpoints update hasn't been applied to the cache yet.
2496
+ pod3 := pod .DeepCopy ()
2497
+ pod3 .ResourceVersion = "3"
2498
+ pod3 .Status .Conditions [0 ].Status = v1 .ConditionTrue
2499
+ _ = controller .podStore .Update (pod3 )
2500
+ controller .updatePod (pod2 , pod3 )
2501
+ // It shouldn't get endpoints request as the endpoints in the cache is out-of-date.
2502
+ timer := time .NewTimer (100 * time .Millisecond )
2503
+ select {
2504
+ case <- timer .C :
2505
+ case <- blockNextAction :
2506
+ t .Errorf ("Pod Update shouldn't have caused a request to be sent to the test server" )
2507
+ }
2508
+
2509
+ // Applying the endpoints update to the cache should cause test server to update endpoints.
2510
+ close (blockUpdate )
2511
+ waitForChanReceive (t , 1 * time .Second , blockNextAction , "Endpoints should have been updated" )
2512
+
2513
+ close (blockNextAction )
2514
+ close (stopChan )
2515
+ }
2516
+
2426
2517
func TestSyncServiceAddresses (t * testing.T ) {
2427
2518
makeService := func (tolerateUnready bool ) * v1.Service {
2428
2519
return & v1.Service {
0 commit comments