2014-06-06 19:40:48 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2014 The Kubernetes Authors .
2014-06-06 19:40:48 -04:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
2014-06-23 14:32:11 -04:00
2015-10-09 23:58:57 -04:00
package endpoint
2014-06-06 19:40:48 -04:00
import (
2021-03-08 20:54:18 -05:00
"context"
2014-06-06 19:40:48 -04:00
"fmt"
2014-08-28 00:32:52 -04:00
"net/http"
2014-07-18 16:22:26 -04:00
"net/http/httptest"
2019-11-15 15:30:42 -05:00
"reflect"
2019-07-24 05:01:42 -04:00
"strconv"
2014-06-06 19:40:48 -04:00
"testing"
2017-06-19 11:47:29 -04:00
"time"
2014-06-06 19:40:48 -04:00
2019-07-24 05:01:42 -04:00
v1 "k8s.io/api/core/v1"
2017-01-11 09:09:48 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
2018-05-01 10:54:37 -04:00
"k8s.io/apimachinery/pkg/runtime/schema"
2019-11-15 15:30:42 -05:00
"k8s.io/apimachinery/pkg/util/diff"
2017-01-27 15:42:17 -05:00
"k8s.io/apimachinery/pkg/util/intstr"
2017-06-19 11:47:29 -04:00
"k8s.io/apimachinery/pkg/util/wait"
2017-06-23 16:56:37 -04:00
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
2021-03-08 20:54:18 -05:00
"k8s.io/client-go/kubernetes/fake"
2019-12-13 11:28:11 -05:00
clientscheme "k8s.io/client-go/kubernetes/scheme"
2017-01-19 13:27:59 -05:00
restclient "k8s.io/client-go/rest"
2017-01-24 09:11:51 -05:00
"k8s.io/client-go/tools/cache"
2017-01-23 13:37:22 -05:00
utiltesting "k8s.io/client-go/util/testing"
2016-11-18 15:50:17 -05:00
endptspkg "k8s.io/kubernetes/pkg/api/v1/endpoints"
2017-11-08 17:34:54 -05:00
api "k8s.io/kubernetes/pkg/apis/core"
2020-10-01 03:08:48 -04:00
controllerpkg "k8s.io/kubernetes/pkg/controller"
2020-05-24 18:27:20 -04:00
utilnet "k8s.io/utils/net"
2020-02-18 20:30:57 -05:00
utilpointer "k8s.io/utils/pointer"
2014-06-06 19:40:48 -04:00
)
2016-03-16 04:47:30 -04:00
var alwaysReady = func ( ) bool { return true }
2017-06-19 11:47:29 -04:00
var neverReady = func ( ) bool { return false }
2016-08-15 18:25:59 -04:00
var emptyNodeName string
2019-01-24 10:37:58 -05:00
var triggerTime = time . Date ( 2018 , 01 , 01 , 0 , 0 , 0 , 0 , time . UTC )
var triggerTimeString = triggerTime . Format ( time . RFC3339Nano )
var oldTriggerTimeString = triggerTime . Add ( - time . Hour ) . Format ( time . RFC3339Nano )
2016-03-16 04:47:30 -04:00
2020-05-24 18:27:20 -04:00
var ipv4only = [ ] v1 . IPFamily { v1 . IPv4Protocol }
var ipv6only = [ ] v1 . IPFamily { v1 . IPv6Protocol }
var ipv4ipv6 = [ ] v1 . IPFamily { v1 . IPv4Protocol , v1 . IPv6Protocol }
var ipv6ipv4 = [ ] v1 . IPFamily { v1 . IPv6Protocol , v1 . IPv4Protocol }
func testPod ( namespace string , id int , nPorts int , isReady bool , ipFamilies [ ] v1 . IPFamily ) * v1 . Pod {
2019-07-24 05:01:42 -04:00
p := & v1 . Pod {
TypeMeta : metav1 . TypeMeta { APIVersion : "v1" } ,
ObjectMeta : metav1 . ObjectMeta {
2022-02-17 01:10:49 -05:00
Namespace : namespace ,
Name : fmt . Sprintf ( "pod%d" , id ) ,
Labels : map [ string ] string { "foo" : "bar" } ,
ResourceVersion : fmt . Sprint ( id ) ,
2019-07-24 05:01:42 -04:00
} ,
Spec : v1 . PodSpec {
Containers : [ ] v1 . Container { { Ports : [ ] v1 . ContainerPort { } } } ,
} ,
Status : v1 . PodStatus {
Conditions : [ ] v1 . PodCondition {
{
Type : v1 . PodReady ,
Status : v1 . ConditionTrue ,
2015-02-02 13:51:52 -05:00
} ,
2014-07-18 16:22:26 -04:00
} ,
2019-07-24 05:01:42 -04:00
} ,
}
if ! isReady {
p . Status . Conditions [ 0 ] . Status = v1 . ConditionFalse
}
for j := 0 ; j < nPorts ; j ++ {
p . Spec . Containers [ 0 ] . Ports = append ( p . Spec . Containers [ 0 ] . Ports ,
v1 . ContainerPort { Name : fmt . Sprintf ( "port%d" , j ) , ContainerPort : int32 ( 8080 + j ) } )
}
2020-05-24 18:27:20 -04:00
for _ , family := range ipFamilies {
var ip string
if family == v1 . IPv4Protocol {
ip = fmt . Sprintf ( "1.2.3.%d" , 4 + id )
} else {
ip = fmt . Sprintf ( "2000::%d" , 4 + id )
}
p . Status . PodIPs = append ( p . Status . PodIPs , v1 . PodIP { IP : ip } )
2019-08-19 16:45:22 -04:00
}
2020-05-24 11:55:09 -04:00
p . Status . PodIP = p . Status . PodIPs [ 0 ] . IP
2019-07-24 05:01:42 -04:00
return p
}
2020-05-24 18:27:20 -04:00
func addPods ( store cache . Store , namespace string , nPods int , nPorts int , nNotReady int , ipFamilies [ ] v1 . IPFamily ) {
2019-07-24 05:01:42 -04:00
for i := 0 ; i < nPods + nNotReady ; i ++ {
isReady := i < nPods
2020-05-24 18:27:20 -04:00
pod := testPod ( namespace , i , nPorts , isReady , ipFamilies )
2019-07-24 05:01:42 -04:00
store . Add ( pod )
2014-07-18 16:22:26 -04:00
}
}
2017-06-14 03:54:33 -04:00
func addNotReadyPodsWithSpecifiedRestartPolicyAndPhase ( store cache . Store , namespace string , nPods int , nPorts int , restartPolicy v1 . RestartPolicy , podPhase v1 . PodPhase ) {
for i := 0 ; i < nPods ; i ++ {
p := & v1 . Pod {
2018-05-01 10:54:37 -04:00
TypeMeta : metav1 . TypeMeta { APIVersion : "v1" } ,
2017-06-14 03:54:33 -04:00
ObjectMeta : metav1 . ObjectMeta {
Namespace : namespace ,
Name : fmt . Sprintf ( "pod%d" , i ) ,
Labels : map [ string ] string { "foo" : "bar" } ,
} ,
Spec : v1 . PodSpec {
RestartPolicy : restartPolicy ,
Containers : [ ] v1 . Container { { Ports : [ ] v1 . ContainerPort { } } } ,
} ,
Status : v1 . PodStatus {
PodIP : fmt . Sprintf ( "1.2.3.%d" , 4 + i ) ,
Phase : podPhase ,
Conditions : [ ] v1 . PodCondition {
{
Type : v1 . PodReady ,
Status : v1 . ConditionFalse ,
} ,
} ,
} ,
}
for j := 0 ; j < nPorts ; j ++ {
p . Spec . Containers [ 0 ] . Ports = append ( p . Spec . Containers [ 0 ] . Ports ,
2019-07-24 05:01:42 -04:00
v1 . ContainerPort { Name : fmt . Sprintf ( "port%d" , j ) , ContainerPort : int32 ( 8080 + j ) } )
2017-06-14 03:54:33 -04:00
}
store . Add ( p )
}
}
2017-06-19 11:47:29 -04:00
func makeTestServer ( t * testing . T , namespace string ) ( * httptest . Server , * utiltesting . FakeHandler ) {
2016-01-15 01:33:50 -05:00
fakeEndpointsHandler := utiltesting . FakeHandler {
2017-06-19 11:47:29 -04:00
StatusCode : http . StatusOK ,
2019-12-13 11:28:11 -05:00
ResponseBody : runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints { } ) ,
2014-09-26 16:34:55 -04:00
}
2014-08-28 00:32:52 -04:00
mux := http . NewServeMux ( )
2019-12-13 11:28:11 -05:00
if namespace == "" {
t . Fatal ( "namespace cannot be empty" )
}
mux . Handle ( "/api/v1/namespaces/" + namespace + "/endpoints" , & fakeEndpointsHandler )
mux . Handle ( "/api/v1/namespaces/" + namespace + "/endpoints/" , & fakeEndpointsHandler )
2014-08-28 00:32:52 -04:00
mux . HandleFunc ( "/" , func ( res http . ResponseWriter , req * http . Request ) {
t . Errorf ( "unexpected request: %v" , req . RequestURI )
2018-10-08 15:20:52 -04:00
http . Error ( res , "" , http . StatusNotFound )
2014-08-28 00:32:52 -04:00
} )
2014-09-26 16:34:55 -04:00
return httptest . NewServer ( mux ) , & fakeEndpointsHandler
2014-08-28 00:32:52 -04:00
}
2020-07-20 20:22:54 -04:00
// makeBlockingEndpointDeleteTestServer will signal the blockNextAction channel on endpoint "POST" & "DELETE" requests. All
// block endpoint "DELETE" requestsi will wait on a blockDelete signal to delete endpoint. If controller is nil, a error will
// be sent in the response.
func makeBlockingEndpointDeleteTestServer ( t * testing . T , controller * endpointController , endpoint * v1 . Endpoints , blockDelete , blockNextAction chan struct { } , namespace string ) * httptest . Server {
handlerFunc := func ( res http . ResponseWriter , req * http . Request ) {
if controller == nil {
res . WriteHeader ( http . StatusInternalServerError )
res . Write ( [ ] byte ( "controller has not been set yet" ) )
return
}
if req . Method == "POST" {
controller . endpointsStore . Add ( endpoint )
blockNextAction <- struct { } { }
}
if req . Method == "DELETE" {
go func ( ) {
// Delay the deletion of endoints to make endpoint cache out of sync
<- blockDelete
controller . endpointsStore . Delete ( endpoint )
controller . onEndpointsDelete ( endpoint )
} ( )
blockNextAction <- struct { } { }
}
res . WriteHeader ( http . StatusOK )
res . Write ( [ ] byte ( runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints { } ) ) )
}
mux := http . NewServeMux ( )
mux . HandleFunc ( "/api/v1/namespaces/" + namespace + "/endpoints" , handlerFunc )
mux . HandleFunc ( "/api/v1/namespaces/" + namespace + "/endpoints/" , handlerFunc )
mux . HandleFunc ( "/api/v1/namespaces/" + namespace + "/events" , func ( res http . ResponseWriter , req * http . Request ) { } )
mux . HandleFunc ( "/" , func ( res http . ResponseWriter , req * http . Request ) {
t . Errorf ( "unexpected request: %v" , req . RequestURI )
http . Error ( res , "" , http . StatusNotFound )
} )
return httptest . NewServer ( mux )
}
2017-02-07 20:25:52 -05:00
type endpointController struct {
2020-10-01 03:08:48 -04:00
* Controller
2017-06-19 11:47:29 -04:00
podStore cache . Store
serviceStore cache . Store
endpointsStore cache . Store
2017-02-07 20:25:52 -05:00
}
2019-07-24 05:01:42 -04:00
func newController ( url string , batchPeriod time . Duration ) * endpointController {
2018-05-01 10:54:37 -04:00
client := clientset . NewForConfigOrDie ( & restclient . Config { Host : url , ContentConfig : restclient . ContentConfig { GroupVersion : & schema . GroupVersion { Group : "" , Version : "v1" } } } )
2020-10-01 03:08:48 -04:00
informerFactory := informers . NewSharedInformerFactory ( client , controllerpkg . NoResyncPeriodFunc ( ) )
2017-06-19 11:47:29 -04:00
endpoints := NewEndpointController ( informerFactory . Core ( ) . V1 ( ) . Pods ( ) , informerFactory . Core ( ) . V1 ( ) . Services ( ) ,
2019-07-24 05:01:42 -04:00
informerFactory . Core ( ) . V1 ( ) . Endpoints ( ) , client , batchPeriod )
2017-02-07 20:25:52 -05:00
endpoints . podsSynced = alwaysReady
endpoints . servicesSynced = alwaysReady
2017-06-19 11:47:29 -04:00
endpoints . endpointsSynced = alwaysReady
2017-02-07 20:25:52 -05:00
return & endpointController {
endpoints ,
informerFactory . Core ( ) . V1 ( ) . Pods ( ) . Informer ( ) . GetStore ( ) ,
informerFactory . Core ( ) . V1 ( ) . Services ( ) . Informer ( ) . GetStore ( ) ,
2017-06-19 11:47:29 -04:00
informerFactory . Core ( ) . V1 ( ) . Endpoints ( ) . Informer ( ) . GetStore ( ) ,
2017-02-07 20:25:52 -05:00
}
}
2021-03-08 20:54:18 -05:00
func newFakeController ( batchPeriod time . Duration ) ( * fake . Clientset , * endpointController ) {
client := fake . NewSimpleClientset ( )
informerFactory := informers . NewSharedInformerFactory ( client , controllerpkg . NoResyncPeriodFunc ( ) )
eController := NewEndpointController (
informerFactory . Core ( ) . V1 ( ) . Pods ( ) ,
informerFactory . Core ( ) . V1 ( ) . Services ( ) ,
informerFactory . Core ( ) . V1 ( ) . Endpoints ( ) ,
client ,
batchPeriod )
eController . podsSynced = alwaysReady
eController . servicesSynced = alwaysReady
eController . endpointsSynced = alwaysReady
return client , & endpointController {
eController ,
informerFactory . Core ( ) . V1 ( ) . Pods ( ) . Informer ( ) . GetStore ( ) ,
informerFactory . Core ( ) . V1 ( ) . Services ( ) . Informer ( ) . GetStore ( ) ,
informerFactory . Core ( ) . V1 ( ) . Endpoints ( ) . Informer ( ) . GetStore ( ) ,
}
}
2014-11-18 12:49:00 -05:00
func TestSyncEndpointsItemsPreserveNoSelector ( t * testing . T ) {
2017-01-21 22:36:02 -05:00
ns := metav1 . NamespaceDefault
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 } } ,
} } ,
} )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec { Ports : [ ] v1 . ServicePort { { Port : 80 } } } ,
2015-04-16 19:18:02 -04:00
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2015-02-18 17:43:37 -05:00
endpointsHandler . ValidateRequestCount ( t , 0 )
}
2017-05-12 13:01:54 -04:00
func TestSyncEndpointsExistingNilSubsets ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-05-12 13:01:54 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : nil ,
} )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-05-12 13:01:54 -04:00
endpointsHandler . ValidateRequestCount ( t , 0 )
}
func TestSyncEndpointsExistingEmptySubsets ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-05-12 13:01:54 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-05-12 13:01:54 -04:00
endpointsHandler . ValidateRequestCount ( t , 0 )
}
2022-02-11 10:43:36 -05:00
func TestSyncEndpointsWithPodResourceVersionUpdateOnly ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
pod0 := testPod ( ns , 0 , 1 , true , ipv4only )
pod1 := testPod ( ns , 1 , 1 , false , ipv4only )
endpoints := newController ( testServer . URL , 0 * time . Second )
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress {
{
IP : pod0 . Status . PodIPs [ 0 ] . IP ,
NodeName : & emptyNodeName ,
TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : pod0 . Name , Namespace : ns , ResourceVersion : "1" } ,
} ,
} ,
NotReadyAddresses : [ ] v1 . EndpointAddress {
{
IP : pod1 . Status . PodIPs [ 0 ] . IP ,
NodeName : & emptyNodeName ,
TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : pod1 . Name , Namespace : ns , ResourceVersion : "2" } ,
} ,
} ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
} } ,
} )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
} ,
} )
pod0 . ResourceVersion = "3"
pod1 . ResourceVersion = "4"
endpoints . podStore . Add ( pod0 )
endpoints . podStore . Add ( pod1 )
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
endpointsHandler . ValidateRequestCount ( t , 0 )
}
2017-05-12 13:01:54 -04:00
func TestSyncEndpointsNewNoSubsets ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-05-12 13:01:54 -04:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-05-12 13:01:54 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
}
2015-04-24 17:16:27 -04:00
func TestCheckLeftoverEndpoints ( t * testing . T ) {
2017-01-21 22:36:02 -05:00
ns := metav1 . NamespaceDefault
2017-06-19 11:47:29 -04:00
testServer , _ := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 } } ,
} } ,
} )
2015-04-24 17:16:27 -04:00
endpoints . checkLeftoverEndpoints ( )
if e , a := 1 , endpoints . queue . Len ( ) ; e != a {
t . Fatalf ( "Expected %v, got %v" , e , a )
}
got , _ := endpoints . queue . Get ( )
if e , a := ns + "/foo" , got ; e != a {
t . Errorf ( "Expected %v, got %v" , e , a )
}
}
2015-02-18 17:43:37 -05:00
func TestSyncEndpointsProtocolTCP ( t * testing . T ) {
2015-04-16 19:18:02 -04:00
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "TCP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "TCP" } } ,
2015-04-16 19:18:02 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-13 20:43:52 -05:00
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2016-01-13 20:43:52 -05:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
2016-01-13 20:43:52 -05:00
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2015-02-18 17:43:37 -05:00
}
2020-08-19 13:16:29 -04:00
func TestSyncEndpointsHeadlessServiceLabel ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
endpoints := newController ( testServer . URL , 0 * time . Second )
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2020-08-19 13:16:29 -04:00
endpointsHandler . ValidateRequestCount ( t , 0 )
}
2015-02-18 17:43:37 -05:00
func TestSyncEndpointsProtocolUDP ( t * testing . T ) {
2015-04-16 19:18:02 -04:00
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "UDP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "UDP" } } ,
2015-04-16 19:18:02 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-01-13 20:43:52 -05:00
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2016-01-13 20:43:52 -05:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "UDP" } } ,
2016-01-13 20:43:52 -05:00
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2014-11-18 12:49:00 -05:00
}
2018-06-11 07:25:18 -04:00
func TestSyncEndpointsProtocolSCTP ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2018-06-11 07:25:18 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "SCTP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2018-06-11 07:25:18 -04:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "SCTP" } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2018-06-11 07:25:18 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2018-06-11 07:25:18 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2018-06-11 07:25:18 -04:00
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "SCTP" } } ,
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2018-06-11 07:25:18 -04:00
}
2014-11-18 12:49:00 -05:00
func TestSyncEndpointsItemsEmptySelectorSelectsAll ( t * testing . T ) {
2015-04-16 19:18:02 -04:00
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
2015-04-16 19:18:02 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-11-18 12:49:00 -05:00
Name : "foo" ,
2015-04-16 19:18:02 -04:00
Namespace : ns ,
2014-11-18 12:49:00 -05:00
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2014-11-18 12:49:00 -05:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
2015-02-17 08:24:05 -05:00
} } ,
2014-11-18 12:49:00 -05:00
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2014-11-18 12:49:00 -05:00
}
2015-09-09 21:28:53 -04:00
func TestSyncEndpointsItemsEmptySelectorSelectsAllNotReady ( t * testing . T ) {
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 0 , 1 , 1 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-09-09 21:28:53 -04:00
Selector : map [ string ] string { } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
2015-09-09 21:28:53 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-09 21:28:53 -04:00
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2015-09-09 21:28:53 -04:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
NotReadyAddresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
2015-09-09 21:28:53 -04:00
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2015-09-09 21:28:53 -04:00
}
func TestSyncEndpointsItemsEmptySelectorSelectsAllMixed ( t * testing . T ) {
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 1 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-09-09 21:28:53 -04:00
Selector : map [ string ] string { } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
2015-09-09 21:28:53 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-09 21:28:53 -04:00
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2015-09-09 21:28:53 -04:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
NotReadyAddresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.5" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod1" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
2015-09-09 21:28:53 -04:00
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2015-09-09 21:28:53 -04:00
}
2014-09-26 16:34:55 -04:00
func TestSyncEndpointsItemsPreexisting ( t * testing . T ) {
2015-04-16 19:18:02 -04:00
ns := "bar"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { "foo" : "bar" } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
2015-04-16 19:18:02 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-22 13:02:02 -04:00
Name : "foo" ,
2015-04-16 19:18:02 -04:00
Namespace : ns ,
2014-10-07 16:51:28 -04:00
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2014-09-26 16:34:55 -04:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
2015-02-17 08:24:05 -05:00
} } ,
2014-09-26 16:34:55 -04:00
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2014-09-26 16:34:55 -04:00
}
func TestSyncEndpointsItemsPreexistingIdentical ( t * testing . T ) {
2017-01-21 22:36:02 -05:00
ns := metav1 . NamespaceDefault
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
ResourceVersion : "1" ,
Name : "foo" ,
Namespace : ns ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , metav1 . NamespaceDefault , 1 , 1 , 0 , ipv4only )
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-21 22:36:02 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : metav1 . NamespaceDefault } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { "foo" : "bar" } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
2015-04-16 19:18:02 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
endpointsHandler . ValidateRequestCount ( t , 0 )
2014-09-26 16:34:55 -04:00
}
func TestSyncEndpointsItems ( t * testing . T ) {
2015-04-16 19:18:02 -04:00
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 3 , 2 , 0 , ipv4only )
addPods ( endpoints . podStore , "blah" , 5 , 2 , 0 , ipv4only ) // make sure these aren't found!
2019-08-19 16:45:22 -04:00
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { "foo" : "bar" } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort {
2015-11-10 01:28:45 -05:00
{ Name : "port0" , Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } ,
{ Name : "port1" , Port : 88 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8088 ) } ,
2015-04-16 19:18:02 -04:00
} ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , "other/foo" )
2017-06-19 11:47:29 -04:00
2016-11-18 15:50:17 -05:00
expectedSubsets := [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress {
{ IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } ,
{ IP : "1.2.3.5" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod1" , Namespace : ns } } ,
{ IP : "1.2.3.6" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod2" , Namespace : ns } } ,
2015-03-20 17:24:43 -04:00
} ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . EndpointPort {
2015-03-13 11:16:41 -04:00
{ Name : "port0" , Port : 8080 , Protocol : "TCP" } ,
{ Name : "port1" , Port : 8088 , Protocol : "TCP" } ,
2015-03-20 17:24:43 -04:00
} ,
} }
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2014-10-07 16:51:28 -04:00
ResourceVersion : "" ,
2017-06-19 11:47:29 -04:00
Name : "foo" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2014-09-26 16:34:55 -04:00
} ,
2015-03-20 17:24:43 -04:00
Subsets : endptspkg . SortSubsets ( expectedSubsets ) ,
2014-09-26 16:34:55 -04:00
} )
2017-06-19 11:47:29 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints" , "POST" , & data )
2014-06-06 19:40:48 -04:00
}
2015-04-16 19:18:02 -04:00
func TestSyncEndpointsItemsWithLabels ( t * testing . T ) {
ns := "other"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 3 , 2 , 0 , ipv4only )
2015-04-16 19:18:02 -04:00
serviceLabels := map [ string ] string { "foo" : "bar" }
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-16 19:18:02 -04:00
Name : "foo" ,
Namespace : ns ,
Labels : serviceLabels ,
} ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { "foo" : "bar" } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort {
2015-11-10 01:28:45 -05:00
{ Name : "port0" , Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } ,
{ Name : "port1" , Port : 88 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8088 ) } ,
2015-04-06 16:14:54 -04:00
} ,
} ,
2015-04-16 19:18:02 -04:00
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
2016-11-18 15:50:17 -05:00
expectedSubsets := [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress {
{ IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } ,
{ IP : "1.2.3.5" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod1" , Namespace : ns } } ,
{ IP : "1.2.3.6" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod2" , Namespace : ns } } ,
2015-04-06 16:14:54 -04:00
} ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . EndpointPort {
2015-04-06 16:14:54 -04:00
{ Name : "port0" , Port : 8080 , Protocol : "TCP" } ,
{ Name : "port1" , Port : 8088 , Protocol : "TCP" } ,
} ,
} }
2019-08-19 15:55:37 -04:00
serviceLabels [ v1 . IsHeadlessService ] = ""
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-06 16:14:54 -04:00
ResourceVersion : "" ,
2017-06-19 11:47:29 -04:00
Name : "foo" ,
2015-04-16 19:18:02 -04:00
Labels : serviceLabels ,
2015-04-06 16:14:54 -04:00
} ,
Subsets : endptspkg . SortSubsets ( expectedSubsets ) ,
} )
2017-06-19 11:47:29 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints" , "POST" , & data )
2015-04-06 16:14:54 -04:00
}
func TestSyncEndpointsItemsPreexistingLabelsChange ( t * testing . T ) {
2015-04-16 19:18:02 -04:00
ns := "bar"
2017-06-19 11:47:29 -04:00
testServer , endpointsHandler := makeTestServer ( t , ns )
2016-04-21 07:50:55 -04:00
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-19 11:47:29 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2015-04-16 19:18:02 -04:00
serviceLabels := map [ string ] string { "baz" : "blah" }
2017-02-07 20:25:52 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-16 19:18:02 -04:00
Name : "foo" ,
Namespace : ns ,
Labels : serviceLabels ,
} ,
2016-11-18 15:50:17 -05:00
Spec : v1 . ServiceSpec {
2015-04-16 19:18:02 -04:00
Selector : map [ string ] string { "foo" : "bar" } ,
2016-11-18 15:50:17 -05:00
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
2015-04-16 19:18:02 -04:00
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2017-06-19 11:47:29 -04:00
2019-08-19 15:55:37 -04:00
serviceLabels [ v1 . IsHeadlessService ] = ""
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-06 16:14:54 -04:00
Name : "foo" ,
2015-04-16 19:18:02 -04:00
Namespace : ns ,
2015-04-06 16:14:54 -04:00
ResourceVersion : "1" ,
2015-04-16 19:18:02 -04:00
Labels : serviceLabels ,
2015-04-06 16:14:54 -04:00
} ,
2016-11-18 15:50:17 -05:00
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
2015-04-06 16:14:54 -04:00
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2015-04-06 16:14:54 -04:00
}
2017-06-19 11:47:29 -04:00
func TestWaitsForAllInformersToBeSynced2 ( t * testing . T ) {
var tests = [ ] struct {
podsSynced func ( ) bool
servicesSynced func ( ) bool
endpointsSynced func ( ) bool
shouldUpdateEndpoints bool
} {
{ neverReady , alwaysReady , alwaysReady , false } ,
{ alwaysReady , neverReady , alwaysReady , false } ,
{ alwaysReady , alwaysReady , neverReady , false } ,
{ alwaysReady , alwaysReady , alwaysReady , true } ,
}
for _ , test := range tests {
func ( ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2019-08-19 16:45:22 -04:00
2017-06-19 11:47:29 -04:00
service := & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "TCP" } } ,
} ,
}
endpoints . serviceStore . Add ( service )
2019-10-22 07:57:28 -04:00
endpoints . onServiceUpdate ( service )
2017-06-19 11:47:29 -04:00
endpoints . podsSynced = test . podsSynced
endpoints . servicesSynced = test . servicesSynced
endpoints . endpointsSynced = test . endpointsSynced
endpoints . workerLoopPeriod = 10 * time . Millisecond
stopCh := make ( chan struct { } )
defer close ( stopCh )
2021-04-22 14:27:59 -04:00
go endpoints . Run ( context . TODO ( ) , 1 )
2017-06-19 11:47:29 -04:00
2019-08-16 15:15:53 -04:00
// cache.WaitForNamedCacheSync has a 100ms poll period, and the endpoints worker has a 10ms period.
2017-06-19 11:47:29 -04:00
// To ensure we get all updates, including unexpected ones, we need to wait at least as long as
// a single cache sync period and worker period, with some fudge room.
time . Sleep ( 150 * time . Millisecond )
if test . shouldUpdateEndpoints {
// Ensure the work queue has been processed by looping for up to a second to prevent flakes.
wait . PollImmediate ( 50 * time . Millisecond , 1 * time . Second , func ( ) ( bool , error ) {
return endpoints . queue . Len ( ) == 0 , nil
} )
endpointsHandler . ValidateRequestCount ( t , 1 )
} else {
endpointsHandler . ValidateRequestCount ( t , 0 )
}
} ( )
}
}
2017-06-09 11:22:37 -04:00
func TestSyncEndpointsHeadlessService ( t * testing . T ) {
ns := "headless"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-09 11:22:37 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "TCP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2019-11-15 15:30:42 -05:00
service := & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns , Labels : map [ string ] string { "a" : "b" } } ,
2017-06-09 11:22:37 -04:00
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { } ,
ClusterIP : api . ClusterIPNone ,
Ports : [ ] v1 . ServicePort { } ,
} ,
2019-11-15 15:30:42 -05:00
}
originalService := service . DeepCopy ( )
endpoints . serviceStore . Add ( service )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-06-09 11:22:37 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
2019-11-15 15:30:42 -05:00
"a" : "b" ,
2019-08-19 15:55:37 -04:00
v1 . IsHeadlessService : "" ,
} ,
2017-06-09 11:22:37 -04:00
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
2018-04-12 18:42:26 -04:00
Ports : [ ] v1 . EndpointPort { } ,
2017-06-09 11:22:37 -04:00
} } ,
} )
2019-11-15 15:30:42 -05:00
if ! reflect . DeepEqual ( originalService , service ) {
t . Fatalf ( "syncing endpoints changed service: %s" , diff . ObjectReflectDiff ( service , originalService ) )
}
2017-06-09 11:22:37 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2017-06-09 11:22:37 -04:00
}
2017-06-14 03:54:33 -04:00
func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseFailed ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-14 03:54:33 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
addNotReadyPodsWithSpecifiedRestartPolicyAndPhase ( endpoints . podStore , ns , 1 , 1 , v1 . RestartPolicyNever , v1 . PodFailed )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-06-14 03:54:33 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2017-06-14 03:54:33 -04:00
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2017-06-14 03:54:33 -04:00
}
func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyNeverAndPhaseSucceeded ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-14 03:54:33 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
addNotReadyPodsWithSpecifiedRestartPolicyAndPhase ( endpoints . podStore , ns , 1 , 1 , v1 . RestartPolicyNever , v1 . PodSucceeded )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-06-14 03:54:33 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2017-06-14 03:54:33 -04:00
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2017-06-14 03:54:33 -04:00
}
func TestSyncEndpointsItemsExcludeNotReadyPodsWithRestartPolicyOnFailureAndPhaseSucceeded ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2017-06-14 03:54:33 -04:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
addNotReadyPodsWithSpecifiedRestartPolicyAndPhase ( endpoints . podStore , ns , 1 , 1 , v1 . RestartPolicyOnFailure , v1 . PodSucceeded )
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , Protocol : "TCP" , TargetPort : intstr . FromInt ( 8080 ) } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2017-06-14 03:54:33 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2017-06-14 03:54:33 -04:00
} ,
Subsets : [ ] v1 . EndpointSubset { } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2017-06-14 03:54:33 -04:00
}
2018-08-20 18:32:52 -04:00
func TestSyncEndpointsHeadlessWithoutPort ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2018-08-20 18:32:52 -04:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
ClusterIP : "None" ,
Ports : nil ,
} ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2018-08-20 18:32:52 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2018-08-20 18:32:52 -04:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2018-08-20 18:32:52 -04:00
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : nil ,
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints" , "POST" , & data )
2018-08-20 18:32:52 -04:00
}
2017-06-14 03:54:33 -04:00
// There are 3*5 possibilities(3 types of RestartPolicy by 5 types of PodPhase). Not list them all here.
// Just list all of the 3 false cases and 3 of the 12 true cases.
func TestShouldPodBeInEndpoints ( t * testing . T ) {
testCases := [ ] struct {
name string
pod * v1 . Pod
expected bool
} {
// Pod should not be in endpoints cases:
{
name : "Failed pod with Never RestartPolicy" ,
pod : & v1 . Pod {
Spec : v1 . PodSpec {
RestartPolicy : v1 . RestartPolicyNever ,
} ,
Status : v1 . PodStatus {
Phase : v1 . PodFailed ,
} ,
} ,
expected : false ,
} ,
{
name : "Succeeded pod with Never RestartPolicy" ,
pod : & v1 . Pod {
Spec : v1 . PodSpec {
RestartPolicy : v1 . RestartPolicyNever ,
} ,
Status : v1 . PodStatus {
Phase : v1 . PodSucceeded ,
} ,
} ,
expected : false ,
} ,
{
name : "Succeeded pod with OnFailure RestartPolicy" ,
pod : & v1 . Pod {
Spec : v1 . PodSpec {
RestartPolicy : v1 . RestartPolicyOnFailure ,
} ,
Status : v1 . PodStatus {
Phase : v1 . PodSucceeded ,
} ,
} ,
expected : false ,
} ,
// Pod should be in endpoints cases:
{
name : "Failed pod with Always RestartPolicy" ,
pod : & v1 . Pod {
Spec : v1 . PodSpec {
RestartPolicy : v1 . RestartPolicyAlways ,
} ,
Status : v1 . PodStatus {
Phase : v1 . PodFailed ,
} ,
} ,
expected : true ,
} ,
{
name : "Pending pod with Never RestartPolicy" ,
pod : & v1 . Pod {
Spec : v1 . PodSpec {
RestartPolicy : v1 . RestartPolicyNever ,
} ,
Status : v1 . PodStatus {
Phase : v1 . PodPending ,
} ,
} ,
expected : true ,
} ,
{
name : "Unknown pod with OnFailure RestartPolicy" ,
pod : & v1 . Pod {
Spec : v1 . PodSpec {
RestartPolicy : v1 . RestartPolicyOnFailure ,
} ,
Status : v1 . PodStatus {
Phase : v1 . PodUnknown ,
} ,
} ,
expected : true ,
} ,
}
for _ , test := range testCases {
result := shouldPodBeInEndpoints ( test . pod )
if result != test . expected {
t . Errorf ( "%s: expected : %t, got: %t" , test . name , test . expected , result )
}
}
}
2020-05-24 18:27:20 -04:00
2019-08-19 16:45:22 -04:00
func TestPodToEndpointAddressForService ( t * testing . T ) {
2020-05-24 18:27:20 -04:00
ipv4 := v1 . IPv4Protocol
ipv6 := v1 . IPv6Protocol
2019-08-19 16:45:22 -04:00
testCases := [ ] struct {
2021-09-24 19:30:22 -04:00
name string
ipFamilies [ ] v1 . IPFamily
service v1 . Service
2020-05-24 18:27:20 -04:00
expectedEndpointFamily v1 . IPFamily
expectError bool
2019-08-19 16:45:22 -04:00
} {
{
2021-09-24 19:30:22 -04:00
name : "v4 service, in a single stack cluster" ,
ipFamilies : ipv4only ,
2019-08-19 16:45:22 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : "10.0.0.1" ,
} ,
} ,
2020-05-24 18:27:20 -04:00
expectedEndpointFamily : ipv4 ,
2019-08-19 16:45:22 -04:00
} ,
{
2021-09-24 19:30:22 -04:00
name : "v4 service, in a dual stack cluster" ,
ipFamilies : ipv4ipv6 ,
2019-08-19 16:45:22 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : "10.0.0.1" ,
} ,
} ,
2020-05-24 18:27:20 -04:00
expectedEndpointFamily : ipv4 ,
} ,
{
2021-09-24 19:30:22 -04:00
name : "v4 service, in a dual stack ipv6-primary cluster" ,
ipFamilies : ipv6ipv4 ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : "10.0.0.1" ,
} ,
} ,
expectedEndpointFamily : ipv4 ,
} ,
{
2021-09-24 19:30:22 -04:00
name : "v4 headless service, in a single stack cluster" ,
ipFamilies : ipv4only ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : v1 . ClusterIPNone ,
} ,
} ,
expectedEndpointFamily : ipv4 ,
} ,
{
2021-09-24 19:30:22 -04:00
name : "v4 headless service, in a dual stack cluster" ,
ipFamilies : ipv4ipv6 ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
2020-10-26 16:15:59 -04:00
ClusterIP : v1 . ClusterIPNone ,
IPFamilies : [ ] v1 . IPFamily { v1 . IPv4Protocol } ,
2020-05-24 18:27:20 -04:00
} ,
} ,
expectedEndpointFamily : ipv4 ,
} ,
{
2021-09-24 19:30:22 -04:00
name : "v4 legacy headless service, in a dual stack cluster" ,
ipFamilies : ipv4ipv6 ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : v1 . ClusterIPNone ,
} ,
} ,
expectedEndpointFamily : ipv4 ,
2019-08-19 16:45:22 -04:00
} ,
{
2021-09-24 19:30:22 -04:00
name : "v4 legacy headless service, in a dual stack ipv6-primary cluster" ,
ipFamilies : ipv6ipv4 ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : v1 . ClusterIPNone ,
} ,
} ,
dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
2020-10-26 16:15:59 -04:00
expectedEndpointFamily : ipv6 ,
2020-05-24 18:27:20 -04:00
} ,
{
2021-09-24 19:30:22 -04:00
name : "v6 service, in a dual stack cluster" ,
ipFamilies : ipv4ipv6 ,
2019-08-19 16:45:22 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : "3000::1" ,
} ,
} ,
2020-05-24 18:27:20 -04:00
expectedEndpointFamily : ipv6 ,
} ,
{
2021-09-24 19:30:22 -04:00
name : "v6 headless service, in a single stack cluster" ,
ipFamilies : ipv6only ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : v1 . ClusterIPNone ,
} ,
} ,
expectedEndpointFamily : ipv6 ,
} ,
2020-05-24 17:48:59 -04:00
{
2021-09-24 19:30:22 -04:00
name : "v6 headless service, in a dual stack cluster (connected to a new api-server)" ,
ipFamilies : ipv4ipv6 ,
2020-05-24 17:48:59 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
2020-10-26 16:15:59 -04:00
ClusterIP : v1 . ClusterIPNone ,
IPFamilies : [ ] v1 . IPFamily { v1 . IPv6Protocol } , // <- set by a api-server defaulting logic
2020-05-24 17:48:59 -04:00
} ,
} ,
expectedEndpointFamily : ipv6 ,
} ,
2020-05-24 18:27:20 -04:00
{
2021-09-24 19:30:22 -04:00
name : "v6 legacy headless service, in a dual stack cluster (connected to a old api-server)" ,
ipFamilies : ipv4ipv6 ,
2020-05-24 18:27:20 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
dual stack services (#91824)
* api: structure change
* api: defaulting, conversion, and validation
* [FIX] validation: auto remove second ip/family when service changes to SingleStack
* [FIX] api: defaulting, conversion, and validation
* api-server: clusterIPs alloc, printers, storage and strategy
* [FIX] clusterIPs default on read
* alloc: auto remove second ip/family when service changes to SingleStack
* api-server: repair loop handling for clusterIPs
* api-server: force kubernetes default service into single stack
* api-server: tie dualstack feature flag with endpoint feature flag
* controller-manager: feature flag, endpoint, and endpointSlice controllers handling multi family service
* [FIX] controller-manager: feature flag, endpoint, and endpointSlicecontrollers handling multi family service
* kube-proxy: feature-flag, utils, proxier, and meta proxier
* [FIX] kubeproxy: call both proxier at the same time
* kubenet: remove forced pod IP sorting
* kubectl: modify describe to include ClusterIPs, IPFamilies, and IPFamilyPolicy
* e2e: fix tests that depends on IPFamily field AND add dual stack tests
* e2e: fix expected error message for ClusterIP immutability
* add integration tests for dualstack
the third phase of dual stack is a very complex change in the API,
basically it introduces Dual Stack services. Main changes are:
- It pluralizes the Service IPFamily field to IPFamilies,
and removes the singular field.
- It introduces a new field IPFamilyPolicyType that can take
3 values to express the "dual-stack(mad)ness" of the cluster:
SingleStack, PreferDualStack and RequireDualStack
- It pluralizes ClusterIP to ClusterIPs.
The goal is to add coverage to the services API operations,
taking into account the 6 different modes a cluster can have:
- single stack: IP4 or IPv6 (as of today)
- dual stack: IPv4 only, IPv6 only, IPv4 - IPv6, IPv6 - IPv4
* [FIX] add integration tests for dualstack
* generated data
* generated files
Co-authored-by: Antonio Ojea <aojea@redhat.com>
2020-10-26 16:15:59 -04:00
ClusterIP : v1 . ClusterIPNone , // <- families are not set by api-server
2020-05-24 18:27:20 -04:00
} ,
} ,
expectedEndpointFamily : ipv4 ,
2019-08-19 16:45:22 -04:00
} ,
// in reality this is a misconfigured cluster
// i.e user is not using dual stack and have PodIP == v4 and ServiceIP==v6
2021-09-24 19:30:22 -04:00
// previously controller could assign wrong ip to endpoint address
// with gate removed. this is no longer the case. this is *not* behavior change
// because previously things would have failed in kube-proxy anyway (due to editing wrong iptables).
2019-08-19 16:45:22 -04:00
{
2021-09-24 19:30:22 -04:00
name : "v6 service, in a v4 only cluster." ,
ipFamilies : ipv4only ,
2019-08-19 16:45:22 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : "3000::1" ,
} ,
} ,
2021-09-24 19:30:22 -04:00
expectError : true ,
2020-05-24 18:27:20 -04:00
expectedEndpointFamily : ipv4 ,
2019-08-19 16:45:22 -04:00
} ,
2020-05-24 18:27:20 -04:00
// but this will actually give an error
2019-08-19 16:45:22 -04:00
{
2021-09-24 19:30:22 -04:00
name : "v6 service, in a v4 only cluster" ,
ipFamilies : ipv4only ,
2019-08-19 16:45:22 -04:00
service : v1 . Service {
Spec : v1 . ServiceSpec {
ClusterIP : "3000::1" ,
} ,
} ,
2020-05-24 18:27:20 -04:00
expectError : true ,
2019-08-19 16:45:22 -04:00
} ,
}
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
podStore := cache . NewStore ( cache . DeletionHandlingMetaNamespaceKeyFunc )
ns := "test"
2020-05-24 18:27:20 -04:00
addPods ( podStore , ns , 1 , 1 , 0 , tc . ipFamilies )
2019-08-19 16:45:22 -04:00
pods := podStore . List ( )
if len ( pods ) != 1 {
t . Fatalf ( "podStore size: expected: %d, got: %d" , 1 , len ( pods ) )
}
pod := pods [ 0 ] . ( * v1 . Pod )
epa , err := podToEndpointAddressForService ( & tc . service , pod )
if err != nil && ! tc . expectError {
t . Fatalf ( "podToEndpointAddressForService returned unexpected error %v" , err )
}
if err == nil && tc . expectError {
t . Fatalf ( "podToEndpointAddressForService should have returned error but it did not" )
}
if err != nil && tc . expectError {
return
}
2020-05-24 18:27:20 -04:00
if utilnet . IsIPv6String ( epa . IP ) != ( tc . expectedEndpointFamily == ipv6 ) {
t . Fatalf ( "IP: expected %s, got: %s" , tc . expectedEndpointFamily , epa . IP )
2019-08-19 16:45:22 -04:00
}
if * ( epa . NodeName ) != pod . Spec . NodeName {
t . Fatalf ( "NodeName: expected: %s, got: %s" , pod . Spec . NodeName , * ( epa . NodeName ) )
}
if epa . TargetRef . Kind != "Pod" {
t . Fatalf ( "TargetRef.Kind: expected: %s, got: %s" , "Pod" , epa . TargetRef . Kind )
}
if epa . TargetRef . Namespace != pod . ObjectMeta . Namespace {
2022-02-17 01:10:49 -05:00
t . Fatalf ( "TargetRef.Namespace: expected: %s, got: %s" , pod . ObjectMeta . Namespace , epa . TargetRef . Namespace )
2019-08-19 16:45:22 -04:00
}
if epa . TargetRef . Name != pod . ObjectMeta . Name {
2022-02-17 01:10:49 -05:00
t . Fatalf ( "TargetRef.Name: expected: %s, got: %s" , pod . ObjectMeta . Name , epa . TargetRef . Name )
2019-08-19 16:45:22 -04:00
}
if epa . TargetRef . UID != pod . ObjectMeta . UID {
2022-02-17 01:10:49 -05:00
t . Fatalf ( "TargetRef.UID: expected: %s, got: %s" , pod . ObjectMeta . UID , epa . TargetRef . UID )
2019-08-19 16:45:22 -04:00
}
2022-02-17 01:10:49 -05:00
if epa . TargetRef . ResourceVersion != "" {
t . Fatalf ( "TargetRef.ResourceVersion: expected empty, got: %s" , epa . TargetRef . ResourceVersion )
2019-08-19 16:45:22 -04:00
}
} )
}
}
2017-08-18 16:09:46 -04:00
2019-01-24 10:37:58 -05:00
func TestLastTriggerChangeTimeAnnotation ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2019-01-24 10:37:58 -05:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "TCP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2019-01-24 10:37:58 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns , CreationTimestamp : metav1 . NewTime ( triggerTime ) } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "TCP" } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-01-24 10:37:58 -05:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2019-01-24 10:37:58 -05:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Annotations : map [ string ] string {
v1 . EndpointsLastChangeTriggerTime : triggerTimeString ,
} ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2019-01-24 10:37:58 -05:00
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2019-01-24 10:37:58 -05:00
}
func TestLastTriggerChangeTimeAnnotation_AnnotationOverridden ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2019-01-24 10:37:58 -05:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Annotations : map [ string ] string {
v1 . EndpointsLastChangeTriggerTime : oldTriggerTimeString ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "TCP" } } ,
} } ,
} )
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2019-01-24 10:37:58 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns , CreationTimestamp : metav1 . NewTime ( triggerTime ) } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "TCP" } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-01-24 10:37:58 -05:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2019-01-24 10:37:58 -05:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Annotations : map [ string ] string {
v1 . EndpointsLastChangeTriggerTime : triggerTimeString ,
} ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} ,
2019-01-24 10:37:58 -05:00
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2019-01-24 10:37:58 -05:00
}
2019-02-20 05:48:24 -05:00
func TestLastTriggerChangeTimeAnnotation_AnnotationCleared ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
2019-07-24 05:01:42 -04:00
endpoints := newController ( testServer . URL , 0 * time . Second )
2019-02-20 05:48:24 -05:00
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
Annotations : map [ string ] string {
v1 . EndpointsLastChangeTriggerTime : triggerTimeString ,
} ,
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "6.7.8.9" , NodeName : & emptyNodeName } } ,
Ports : [ ] v1 . EndpointPort { { Port : 1000 , Protocol : "TCP" } } ,
} } ,
} )
// Neither pod nor service has trigger time, this should cause annotation to be cleared.
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , 1 , 1 , 0 , ipv4only )
2019-02-20 05:48:24 -05:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { } ,
Ports : [ ] v1 . ServicePort { { Port : 80 , TargetPort : intstr . FromInt ( 8080 ) , Protocol : "TCP" } } ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-02-20 05:48:24 -05:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
data := runtime . EncodeOrDie ( clientscheme . Codecs . LegacyCodec ( v1 . SchemeGroupVersion ) , & v1 . Endpoints {
2019-02-20 05:48:24 -05:00
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
2019-08-19 15:55:37 -04:00
Labels : map [ string ] string {
v1 . IsHeadlessService : "" ,
} , // Annotation not set anymore.
2019-02-20 05:48:24 -05:00
} ,
Subsets : [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress { { IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } } ,
Ports : [ ] v1 . EndpointPort { { Port : 8080 , Protocol : "TCP" } } ,
} } ,
} )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "PUT" , & data )
2019-02-20 05:48:24 -05:00
}
2019-07-24 05:01:42 -04:00
// TestPodUpdatesBatching verifies that endpoint updates caused by pod updates are batched together.
// This test uses real time.Sleep, as there is no easy way to mock time in endpoints controller now.
// TODO(mborsz): Migrate this test to mock clock when possible.
func TestPodUpdatesBatching ( t * testing . T ) {
type podUpdate struct {
delay time . Duration
podName string
podIP string
}
tests := [ ] struct {
name string
batchPeriod time . Duration
podsCount int
updates [ ] podUpdate
finalDelay time . Duration
wantRequestCount int
} {
{
name : "three updates with no batching" ,
batchPeriod : 0 * time . Second ,
podsCount : 10 ,
updates : [ ] podUpdate {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
podName : "pod0" ,
podIP : "10.0.0.0" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod1" ,
podIP : "10.0.0.1" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod2" ,
podIP : "10.0.0.2" ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 3 ,
} ,
{
name : "three updates in one batch" ,
batchPeriod : 1 * time . Second ,
podsCount : 10 ,
updates : [ ] podUpdate {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
podName : "pod0" ,
podIP : "10.0.0.0" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod1" ,
podIP : "10.0.0.1" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod2" ,
podIP : "10.0.0.2" ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 1 ,
} ,
{
name : "three updates in two batches" ,
batchPeriod : 1 * time . Second ,
podsCount : 10 ,
updates : [ ] podUpdate {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
podName : "pod0" ,
podIP : "10.0.0.0" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod1" ,
podIP : "10.0.0.1" ,
} ,
{
delay : 1 * time . Second ,
podName : "pod2" ,
podIP : "10.0.0.2" ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 2 ,
} ,
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
ns := "other"
resourceVersion := 1
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
endpoints := newController ( testServer . URL , tc . batchPeriod )
stopCh := make ( chan struct { } )
defer close ( stopCh )
endpoints . podsSynced = alwaysReady
endpoints . servicesSynced = alwaysReady
endpoints . endpointsSynced = alwaysReady
endpoints . workerLoopPeriod = 10 * time . Millisecond
2021-04-22 14:27:59 -04:00
go endpoints . Run ( context . TODO ( ) , 1 )
2019-07-24 05:01:42 -04:00
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , tc . podsCount , 1 , 0 , ipv4only )
2019-07-24 05:01:42 -04:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
for _ , update := range tc . updates {
time . Sleep ( update . delay )
old , exists , err := endpoints . podStore . GetByKey ( fmt . Sprintf ( "%s/%s" , ns , update . podName ) )
if err != nil {
t . Fatalf ( "Error while retrieving old value of %q: %v" , update . podName , err )
}
if ! exists {
t . Fatalf ( "Pod %q doesn't exist" , update . podName )
}
oldPod := old . ( * v1 . Pod )
newPod := oldPod . DeepCopy ( )
newPod . Status . PodIP = update . podIP
2020-05-24 11:55:09 -04:00
newPod . Status . PodIPs [ 0 ] . IP = update . podIP
2019-07-24 05:01:42 -04:00
newPod . ResourceVersion = strconv . Itoa ( resourceVersion )
resourceVersion ++
endpoints . podStore . Update ( newPod )
endpoints . updatePod ( oldPod , newPod )
}
time . Sleep ( tc . finalDelay )
endpointsHandler . ValidateRequestCount ( t , tc . wantRequestCount )
} )
}
}
// TestPodAddsBatching verifies that endpoint updates caused by pod addition are batched together.
// This test uses real time.Sleep, as there is no easy way to mock time in endpoints controller now.
// TODO(mborsz): Migrate this test to mock clock when possible.
func TestPodAddsBatching ( t * testing . T ) {
type podAdd struct {
delay time . Duration
}
tests := [ ] struct {
name string
batchPeriod time . Duration
adds [ ] podAdd
finalDelay time . Duration
wantRequestCount int
} {
{
name : "three adds with no batching" ,
batchPeriod : 0 * time . Second ,
adds : [ ] podAdd {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
} ,
{
delay : 100 * time . Millisecond ,
} ,
{
delay : 100 * time . Millisecond ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 3 ,
} ,
{
name : "three adds in one batch" ,
batchPeriod : 1 * time . Second ,
adds : [ ] podAdd {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
} ,
{
delay : 100 * time . Millisecond ,
} ,
{
delay : 100 * time . Millisecond ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 1 ,
} ,
{
name : "three adds in two batches" ,
batchPeriod : 1 * time . Second ,
adds : [ ] podAdd {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
} ,
{
delay : 100 * time . Millisecond ,
} ,
{
delay : 1 * time . Second ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 2 ,
} ,
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
endpoints := newController ( testServer . URL , tc . batchPeriod )
stopCh := make ( chan struct { } )
defer close ( stopCh )
endpoints . podsSynced = alwaysReady
endpoints . servicesSynced = alwaysReady
endpoints . endpointsSynced = alwaysReady
endpoints . workerLoopPeriod = 10 * time . Millisecond
2021-04-22 14:27:59 -04:00
go endpoints . Run ( context . TODO ( ) , 1 )
2019-07-24 05:01:42 -04:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
for i , add := range tc . adds {
time . Sleep ( add . delay )
2020-05-24 18:27:20 -04:00
p := testPod ( ns , i , 1 , true , ipv4only )
2019-07-24 05:01:42 -04:00
endpoints . podStore . Add ( p )
endpoints . addPod ( p )
}
time . Sleep ( tc . finalDelay )
endpointsHandler . ValidateRequestCount ( t , tc . wantRequestCount )
} )
}
}
// TestPodDeleteBatching verifies that endpoint updates caused by pod deletion are batched together.
// This test uses real time.Sleep, as there is no easy way to mock time in endpoints controller now.
// TODO(mborsz): Migrate this test to mock clock when possible.
func TestPodDeleteBatching ( t * testing . T ) {
type podDelete struct {
delay time . Duration
podName string
}
tests := [ ] struct {
name string
batchPeriod time . Duration
podsCount int
deletes [ ] podDelete
finalDelay time . Duration
wantRequestCount int
} {
{
name : "three deletes with no batching" ,
batchPeriod : 0 * time . Second ,
podsCount : 10 ,
deletes : [ ] podDelete {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
podName : "pod0" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod1" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod2" ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 3 ,
} ,
{
name : "three deletes in one batch" ,
batchPeriod : 1 * time . Second ,
podsCount : 10 ,
deletes : [ ] podDelete {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
podName : "pod0" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod1" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod2" ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 1 ,
} ,
{
name : "three deletes in two batches" ,
batchPeriod : 1 * time . Second ,
podsCount : 10 ,
deletes : [ ] podDelete {
{
// endpoints.Run needs ~100 ms to start processing updates.
delay : 200 * time . Millisecond ,
podName : "pod0" ,
} ,
{
delay : 100 * time . Millisecond ,
podName : "pod1" ,
} ,
{
delay : 1 * time . Second ,
podName : "pod2" ,
} ,
} ,
finalDelay : 3 * time . Second ,
wantRequestCount : 2 ,
} ,
}
for _ , tc := range tests {
t . Run ( tc . name , func ( t * testing . T ) {
ns := "other"
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
endpoints := newController ( testServer . URL , tc . batchPeriod )
stopCh := make ( chan struct { } )
defer close ( stopCh )
endpoints . podsSynced = alwaysReady
endpoints . servicesSynced = alwaysReady
endpoints . endpointsSynced = alwaysReady
endpoints . workerLoopPeriod = 10 * time . Millisecond
2021-04-22 14:27:59 -04:00
go endpoints . Run ( context . TODO ( ) , 1 )
2019-07-24 05:01:42 -04:00
2020-05-24 18:27:20 -04:00
addPods ( endpoints . podStore , ns , tc . podsCount , 1 , 0 , ipv4only )
2019-07-24 05:01:42 -04:00
endpoints . serviceStore . Add ( & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
} )
for _ , update := range tc . deletes {
time . Sleep ( update . delay )
old , exists , err := endpoints . podStore . GetByKey ( fmt . Sprintf ( "%s/%s" , ns , update . podName ) )
if err != nil {
t . Fatalf ( "Error while retrieving old value of %q: %v" , update . podName , err )
}
if ! exists {
t . Fatalf ( "Pod %q doesn't exist" , update . podName )
}
endpoints . podStore . Delete ( old )
endpoints . deletePod ( old )
}
time . Sleep ( tc . finalDelay )
endpointsHandler . ValidateRequestCount ( t , tc . wantRequestCount )
} )
}
}
2019-07-31 18:09:35 -04:00
func TestSyncEndpointsServiceNotFound ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , endpointsHandler := makeTestServer ( t , ns )
defer testServer . Close ( )
endpoints := newController ( testServer . URL , 0 )
endpoints . endpointsStore . Add ( & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "1" ,
} ,
} )
2021-04-22 14:27:59 -04:00
endpoints . syncService ( context . TODO ( ) , ns + "/foo" )
2019-07-31 18:09:35 -04:00
endpointsHandler . ValidateRequestCount ( t , 1 )
2019-12-13 11:28:11 -05:00
endpointsHandler . ValidateRequest ( t , "/api/v1/namespaces/" + ns + "/endpoints/foo" , "DELETE" , nil )
2019-07-31 18:09:35 -04:00
}
2019-07-30 18:42:01 -04:00
2021-03-08 20:54:18 -05:00
func TestSyncServiceOverCapacity ( t * testing . T ) {
testCases := [ ] struct {
2021-07-06 14:26:46 -04:00
name string
startingAnnotation * string
numExisting int
numDesired int
numDesiredNotReady int
numExpectedReady int
numExpectedNotReady int
expectedAnnotation bool
2021-03-08 20:54:18 -05:00
} { {
2021-07-06 14:26:46 -04:00
name : "empty" ,
startingAnnotation : nil ,
numExisting : 0 ,
numDesired : 0 ,
numExpectedReady : 0 ,
numExpectedNotReady : 0 ,
expectedAnnotation : false ,
2021-03-08 20:54:18 -05:00
} , {
2021-07-06 14:26:46 -04:00
name : "annotation added past capacity, < than maxCapacity of Ready Addresses" ,
startingAnnotation : nil ,
numExisting : maxCapacity - 1 ,
numDesired : maxCapacity - 3 ,
numDesiredNotReady : 4 ,
numExpectedReady : maxCapacity - 3 ,
numExpectedNotReady : 3 ,
expectedAnnotation : true ,
2021-03-08 20:54:18 -05:00
} , {
2021-07-06 14:26:46 -04:00
name : "annotation added past capacity, maxCapacity of Ready Addresses " ,
startingAnnotation : nil ,
numExisting : maxCapacity - 1 ,
numDesired : maxCapacity ,
numDesiredNotReady : 10 ,
numExpectedReady : maxCapacity ,
numExpectedNotReady : 0 ,
expectedAnnotation : true ,
2021-03-08 20:54:18 -05:00
} , {
2021-07-06 14:26:46 -04:00
name : "annotation removed below capacity" ,
startingAnnotation : utilpointer . StringPtr ( "truncated" ) ,
numExisting : maxCapacity - 1 ,
numDesired : maxCapacity - 1 ,
numDesiredNotReady : 0 ,
numExpectedReady : maxCapacity - 1 ,
numExpectedNotReady : 0 ,
expectedAnnotation : false ,
2021-03-08 20:54:18 -05:00
} , {
2021-07-06 14:26:46 -04:00
name : "annotation was set to warning previously, annotation removed at capacity" ,
startingAnnotation : utilpointer . StringPtr ( "warning" ) ,
numExisting : maxCapacity ,
numDesired : maxCapacity ,
numDesiredNotReady : 0 ,
numExpectedReady : maxCapacity ,
numExpectedNotReady : 0 ,
expectedAnnotation : false ,
} , {
name : "annotation was set to warning previously but still over capacity" ,
startingAnnotation : utilpointer . StringPtr ( "warning" ) ,
numExisting : maxCapacity + 1 ,
numDesired : maxCapacity + 1 ,
numDesiredNotReady : 0 ,
numExpectedReady : maxCapacity ,
numExpectedNotReady : 0 ,
expectedAnnotation : true ,
} , {
name : "annotation removed at capacity" ,
startingAnnotation : utilpointer . StringPtr ( "truncated" ) ,
numExisting : maxCapacity ,
numDesired : maxCapacity ,
numDesiredNotReady : 0 ,
numExpectedReady : maxCapacity ,
numExpectedNotReady : 0 ,
expectedAnnotation : false ,
} , {
name : "no endpoints change, annotation value corrected" ,
startingAnnotation : utilpointer . StringPtr ( "invalid" ) ,
numExisting : maxCapacity + 1 ,
numDesired : maxCapacity + 1 ,
numDesiredNotReady : 0 ,
numExpectedReady : maxCapacity ,
numExpectedNotReady : 0 ,
expectedAnnotation : true ,
2021-03-08 20:54:18 -05:00
} }
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
ns := "test"
client , c := newFakeController ( 0 * time . Second )
2021-07-06 14:26:46 -04:00
addPods ( c . podStore , ns , tc . numDesired , 1 , tc . numDesiredNotReady , ipv4only )
2021-03-08 20:54:18 -05:00
pods := c . podStore . List ( )
svc := & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
Ports : [ ] v1 . ServicePort { { Port : 80 } } ,
} ,
}
c . serviceStore . Add ( svc )
subset := v1 . EndpointSubset { }
for i := 0 ; i < tc . numExisting ; i ++ {
pod := pods [ i ] . ( * v1 . Pod )
epa , _ := podToEndpointAddressForService ( svc , pod )
subset . Addresses = append ( subset . Addresses , * epa )
}
endpoints := & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : svc . Name ,
Namespace : ns ,
ResourceVersion : "1" ,
Annotations : map [ string ] string { } ,
} ,
Subsets : [ ] v1 . EndpointSubset { subset } ,
}
if tc . startingAnnotation != nil {
endpoints . Annotations [ v1 . EndpointsOverCapacity ] = * tc . startingAnnotation
}
c . endpointsStore . Add ( endpoints )
client . CoreV1 ( ) . Endpoints ( ns ) . Create ( context . TODO ( ) , endpoints , metav1 . CreateOptions { } )
2021-04-22 14:27:59 -04:00
c . syncService ( context . TODO ( ) , fmt . Sprintf ( "%s/%s" , ns , svc . Name ) )
2021-03-08 20:54:18 -05:00
actualEndpoints , err := client . CoreV1 ( ) . Endpoints ( ns ) . Get ( context . TODO ( ) , endpoints . Name , metav1 . GetOptions { } )
if err != nil {
t . Fatalf ( "unexpected error getting endpoints: %v" , err )
}
actualAnnotation , ok := actualEndpoints . Annotations [ v1 . EndpointsOverCapacity ]
if tc . expectedAnnotation {
if ! ok {
t . Errorf ( "Expected EndpointsOverCapacity annotation to be set" )
2021-07-06 14:26:46 -04:00
} else if actualAnnotation != "truncated" {
t . Errorf ( "Expected EndpointsOverCapacity annotation to be 'truncated', got %s" , actualAnnotation )
2021-03-08 20:54:18 -05:00
}
} else {
if ok {
t . Errorf ( "Expected EndpointsOverCapacity annotation not to be set, got %s" , actualAnnotation )
}
}
2021-07-06 14:26:46 -04:00
numActualReady := 0
numActualNotReady := 0
for _ , subset := range actualEndpoints . Subsets {
numActualReady += len ( subset . Addresses )
numActualNotReady += len ( subset . NotReadyAddresses )
}
if numActualReady != tc . numExpectedReady {
t . Errorf ( "Unexpected number of actual ready Endpoints: got %d endpoints, want %d endpoints" , numActualReady , tc . numExpectedReady )
}
if numActualNotReady != tc . numExpectedNotReady {
t . Errorf ( "Unexpected number of actual not ready Endpoints: got %d endpoints, want %d endpoints" , numActualNotReady , tc . numExpectedNotReady )
}
} )
}
}
func TestTruncateEndpoints ( t * testing . T ) {
testCases := [ ] struct {
desc string
// subsetsReady, subsetsNotReady, expectedReady, expectedNotReady
// must all be the same length
subsetsReady [ ] int
subsetsNotReady [ ] int
expectedReady [ ] int
expectedNotReady [ ] int
} { {
desc : "empty" ,
subsetsReady : [ ] int { } ,
subsetsNotReady : [ ] int { } ,
expectedReady : [ ] int { } ,
expectedNotReady : [ ] int { } ,
} , {
desc : "total endpoints < max capacity" ,
subsetsReady : [ ] int { 50 , 100 , 100 , 100 , 100 } ,
subsetsNotReady : [ ] int { 50 , 100 , 100 , 100 , 100 } ,
expectedReady : [ ] int { 50 , 100 , 100 , 100 , 100 } ,
expectedNotReady : [ ] int { 50 , 100 , 100 , 100 , 100 } ,
} , {
desc : "total endpoints = max capacity" ,
subsetsReady : [ ] int { 100 , 100 , 100 , 100 , 100 } ,
subsetsNotReady : [ ] int { 100 , 100 , 100 , 100 , 100 } ,
expectedReady : [ ] int { 100 , 100 , 100 , 100 , 100 } ,
expectedNotReady : [ ] int { 100 , 100 , 100 , 100 , 100 } ,
} , {
desc : "total ready endpoints < max capacity, but total endpoints > max capacity" ,
subsetsReady : [ ] int { 90 , 110 , 50 , 10 , 20 } ,
subsetsNotReady : [ ] int { 101 , 200 , 200 , 201 , 298 } ,
expectedReady : [ ] int { 90 , 110 , 50 , 10 , 20 } ,
expectedNotReady : [ ] int { 73 , 144 , 144 , 145 , 214 } ,
} , {
desc : "total ready endpoints > max capacity" ,
subsetsReady : [ ] int { 205 , 400 , 402 , 400 , 693 } ,
subsetsNotReady : [ ] int { 100 , 200 , 200 , 200 , 300 } ,
expectedReady : [ ] int { 98 , 191 , 192 , 191 , 328 } ,
expectedNotReady : [ ] int { 0 , 0 , 0 , 0 , 0 } ,
} }
for _ , tc := range testCases {
t . Run ( tc . desc , func ( t * testing . T ) {
var subsets [ ] v1 . EndpointSubset
for subsetIndex , numReady := range tc . subsetsReady {
subset := v1 . EndpointSubset { }
for i := 0 ; i < numReady ; i ++ {
subset . Addresses = append ( subset . Addresses , v1 . EndpointAddress { } )
}
numNotReady := tc . subsetsNotReady [ subsetIndex ]
for i := 0 ; i < numNotReady ; i ++ {
subset . NotReadyAddresses = append ( subset . NotReadyAddresses , v1 . EndpointAddress { } )
}
subsets = append ( subsets , subset )
}
endpoints := & v1 . Endpoints { Subsets : subsets }
truncateEndpoints ( endpoints )
for i , subset := range endpoints . Subsets {
if len ( subset . Addresses ) != tc . expectedReady [ i ] {
t . Errorf ( "Unexpected number of actual ready Endpoints for subset %d: got %d endpoints, want %d endpoints" , i , len ( subset . Addresses ) , tc . expectedReady [ i ] )
}
if len ( subset . NotReadyAddresses ) != tc . expectedNotReady [ i ] {
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 ] )
}
}
2021-03-08 20:54:18 -05:00
} )
}
}
2020-02-18 20:30:57 -05:00
func TestEndpointPortFromServicePort ( t * testing . T ) {
http := utilpointer . StringPtr ( "http" )
testCases := map [ string ] struct {
serviceAppProtocol * string
expectedEndpointsAppProtocol * string
} {
2020-11-06 20:46:32 -05:00
"empty app protocol" : {
2020-02-18 20:30:57 -05:00
serviceAppProtocol : nil ,
expectedEndpointsAppProtocol : nil ,
} ,
2020-11-06 20:46:32 -05:00
"http app protocol" : {
2020-02-18 20:30:57 -05:00
serviceAppProtocol : http ,
expectedEndpointsAppProtocol : http ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
epp := endpointPortFromServicePort ( & v1 . ServicePort { Name : "test" , AppProtocol : tc . serviceAppProtocol } , 80 )
if epp . AppProtocol != tc . expectedEndpointsAppProtocol {
t . Errorf ( "Expected Endpoints AppProtocol to be %s, got %s" , stringVal ( tc . expectedEndpointsAppProtocol ) , stringVal ( epp . AppProtocol ) )
}
} )
}
}
2020-07-20 20:22:54 -04:00
// TestMultipleServiceChanges tests that endpoints that are not created because of an out of sync endpoints cache are eventually recreated
// 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.
// After the service is recreated, the endpoints will be deleted replicating an out of sync cache. Expect that eventually the endpoints will be recreated.
func TestMultipleServiceChanges ( t * testing . T ) {
ns := metav1 . NamespaceDefault
expectedSubsets := [ ] v1 . EndpointSubset { {
Addresses : [ ] v1 . EndpointAddress {
{ IP : "1.2.3.4" , NodeName : & emptyNodeName , TargetRef : & v1 . ObjectReference { Kind : "Pod" , Name : "pod0" , Namespace : ns } } ,
} ,
} }
endpoint := & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns , ResourceVersion : "1" } ,
Subsets : expectedSubsets ,
}
controller := & endpointController { }
blockDelete := make ( chan struct { } )
blockNextAction := make ( chan struct { } )
stopChan := make ( chan struct { } )
testServer := makeBlockingEndpointDeleteTestServer ( t , controller , endpoint , blockDelete , blockNextAction , ns )
defer testServer . Close ( )
* controller = * newController ( testServer . URL , 0 * time . Second )
addPods ( controller . podStore , ns , 1 , 1 , 0 , ipv4only )
2021-04-22 14:27:59 -04:00
go func ( ) { controller . Run ( context . TODO ( ) , 1 ) } ( )
2020-07-20 20:22:54 -04:00
svc := & v1 . Service {
ObjectMeta : metav1 . ObjectMeta { Name : "foo" , Namespace : ns } ,
Spec : v1 . ServiceSpec {
Selector : map [ string ] string { "foo" : "bar" } ,
ClusterIP : "None" ,
Ports : nil ,
} ,
}
controller . serviceStore . Add ( svc )
controller . onServiceUpdate ( svc )
// blockNextAction should eventually unblock once server gets endpoint request.
waitForChanReceive ( t , 1 * time . Second , blockNextAction , "Service Add should have caused a request to be sent to the test server" )
controller . serviceStore . Delete ( svc )
controller . onServiceDelete ( svc )
waitForChanReceive ( t , 1 * time . Second , blockNextAction , "Service Delete should have caused a request to be sent to the test server" )
// If endpoints cache has not updated before service update is registered
// Services add will not trigger a Create endpoint request.
controller . serviceStore . Add ( svc )
controller . onServiceUpdate ( svc )
// Ensure the work queue has been processed by looping for up to a second to prevent flakes.
wait . PollImmediate ( 50 * time . Millisecond , 1 * time . Second , func ( ) ( bool , error ) {
return controller . queue . Len ( ) == 0 , nil
} )
// Cause test server to delete endpoints
close ( blockDelete )
waitForChanReceive ( t , 1 * time . Second , blockNextAction , "Endpoint should have been recreated" )
close ( blockNextAction )
close ( stopChan )
}
func TestEndpointsDeletionEvents ( t * testing . T ) {
ns := metav1 . NamespaceDefault
testServer , _ := makeTestServer ( t , ns )
defer testServer . Close ( )
controller := newController ( testServer . URL , 0 )
store := controller . endpointsStore
ep1 := & v1 . Endpoints {
ObjectMeta : metav1 . ObjectMeta {
Name : "foo" ,
Namespace : ns ,
ResourceVersion : "rv1" ,
} ,
}
// Test Unexpected and Expected Deletes
store . Delete ( ep1 )
controller . onEndpointsDelete ( ep1 )
if controller . queue . Len ( ) != 1 {
t . Errorf ( "Expected one service to be in the queue, found %d" , controller . queue . Len ( ) )
}
}
2020-02-18 20:30:57 -05:00
func stringVal ( str * string ) string {
if str == nil {
return "nil"
}
return * str
}
2020-07-20 20:22:54 -04:00
// waitForChanReceive blocks up to the timeout waiting for the receivingChan to receive
func waitForChanReceive ( t * testing . T , timeout time . Duration , receivingChan chan struct { } , errorMsg string ) {
timer := time . NewTimer ( timeout )
select {
case <- timer . C :
t . Errorf ( errorMsg )
case <- receivingChan :
}
}