diff --git a/go.mod b/go.mod index b68d06a34..de5ddb86a 100644 --- a/go.mod +++ b/go.mod @@ -23,12 +23,12 @@ require ( golang.org/x/sys v0.34.0 google.golang.org/grpc v1.74.2 google.golang.org/protobuf v1.36.6 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 - k8s.io/client-go v0.31.3 - k8s.io/cri-api v0.31.3 + k8s.io/api v0.33.3 + k8s.io/apimachinery v0.33.3 + k8s.io/client-go v0.33.3 + k8s.io/cri-api v0.33.3 k8s.io/klog/v2 v2.130.1 - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 + k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 ) require ( @@ -64,13 +64,10 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/gnostic-models v0.6.9 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/k-sone/critbitgo v1.4.0 // indirect @@ -114,18 +111,18 @@ require ( golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/term v0.33.0 // indirect golang.org/x/text v0.27.0 // indirect - golang.org/x/time v0.5.0 // indirect + golang.org/x/time v0.9.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect - k8s.io/kube-openapi v0.0.0-20240730131305-7a9a4e85957e // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 5dce33991..af3d55bc5 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,6 @@ github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -106,8 +104,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= -github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -116,8 +114,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -129,8 +125,6 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -270,6 +264,8 @@ go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxi go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -314,8 +310,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -357,32 +353,32 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= -k8s.io/cri-api v0.31.3 h1:dsZXzrGrCEwHjsTDlAV7rutEplpMLY8bfNRMIqrtXjo= -k8s.io/cri-api v0.31.3/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/cri-api v0.33.3 h1:aQvK3UxsaVMul4z71lOiblMHdhw9ROaw3Cgg15xDrD4= +k8s.io/cri-api v0.33.3/go.mod h1:OLQvT45OpIA+tv91ZrpuFIGY+Y2Ho23poS7n115Aocs= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240730131305-7a9a4e85957e h1:OnKkExfhk4yxMqvBSPzUfhv3zQ96FWJ+UOZzLrAFyAo= -k8s.io/kube-openapi v0.0.0-20240730131305-7a9a4e85957e/go.mod h1:0CVn9SVo8PeW5/JgsBZZIFmmTk5noOM8WXf2e1tCihE= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= +k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/pkg/cmd/kube-router.go b/pkg/cmd/kube-router.go index fff1c8cac..365720177 100644 --- a/pkg/cmd/kube-router.go +++ b/pkg/cmd/kube-router.go @@ -14,6 +14,7 @@ import ( "github.com/cloudnativelabs/kube-router/v2/pkg/controllers/proxy" "github.com/cloudnativelabs/kube-router/v2/pkg/controllers/routing" "github.com/cloudnativelabs/kube-router/v2/pkg/healthcheck" + "github.com/cloudnativelabs/kube-router/v2/pkg/k8s/indexers" "github.com/cloudnativelabs/kube-router/v2/pkg/metrics" "github.com/cloudnativelabs/kube-router/v2/pkg/options" "github.com/cloudnativelabs/kube-router/v2/pkg/utils" @@ -23,6 +24,7 @@ import ( "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" ) @@ -111,14 +113,23 @@ func (kr *KubeRouter) Run() error { wg.Add(1) go hc.RunServer(stopCh, &wg) + // Setup factory and informers informerFactory := informers.NewSharedInformerFactory(kr.Client, 0) svcInformer := informerFactory.Core().V1().Services().Informer() - epInformer := informerFactory.Core().V1().Endpoints().Informer() epSliceInformer := informerFactory.Discovery().V1().EndpointSlices().Informer() podInformer := informerFactory.Core().V1().Pods().Informer() nodeInformer := informerFactory.Core().V1().Nodes().Informer() nsInformer := informerFactory.Core().V1().Namespaces().Informer() npInformer := informerFactory.Networking().V1().NetworkPolicies().Informer() + + // Add custom indexers + err = epSliceInformer.AddIndexers(map[string]cache.IndexFunc{ + indexers.ServiceNameIndex: indexers.ServiceNameIndexFunc, + }) + if err != nil { + return fmt.Errorf("failed to add indexers to endpoint slice informer: %v", err) + } + informerFactory.Start(stopCh) err = kr.CacheSyncOrTimeout(informerFactory, stopCh) @@ -173,7 +184,7 @@ func (kr *KubeRouter) Run() error { if kr.Config.RunRouter { nrc, err := routing.NewNetworkRoutingController(kr.Client, kr.Config, - nodeInformer, svcInformer, epInformer, &ipsetMutex) + nodeInformer, svcInformer, epSliceInformer, &ipsetMutex) if err != nil { return fmt.Errorf("failed to create network routing controller: %v", err) } @@ -186,7 +197,7 @@ func (kr *KubeRouter) Run() error { if err != nil { return fmt.Errorf("failed to add ServiceEventHandler: %v", err) } - _, err = epInformer.AddEventHandler(nrc.EndpointsEventHandler) + _, err = epSliceInformer.AddEventHandler(nrc.EndpointSliceEventHandler) if err != nil { return fmt.Errorf("failed to add EndpointsEventHandler: %v", err) } diff --git a/pkg/controllers/lballoc/lballoc_test.go b/pkg/controllers/lballoc/lballoc_test.go index c8547e9c7..fe06da192 100644 --- a/pkg/controllers/lballoc/lballoc_test.go +++ b/pkg/controllers/lballoc/lballoc_test.go @@ -1,6 +1,7 @@ package lballoc import ( + "context" "errors" "net" "sync" @@ -636,6 +637,10 @@ func (mf *mockInformer) AddEventHandlerWithResyncPeriod(_ cache.ResourceEventHan return nil, nil } +func (mf *mockInformer) AddEventHandlerWithOptions(_ cache.ResourceEventHandler, _ cache.HandlerOptions) (cache.ResourceEventHandlerRegistration, error) { + return nil, nil +} + func (mf *mockInformer) RemoveEventHandler(_ cache.ResourceEventHandlerRegistration) error { return nil } @@ -651,6 +656,9 @@ func (mf *mockInformer) GetController() cache.Controller { func (mf *mockInformer) Run(_ <-chan struct{}) { } +func (mf *mockInformer) RunWithContext(_ context.Context) { +} + func (mf *mockInformer) HasSynced() bool { return false } @@ -663,6 +671,10 @@ func (mf *mockInformer) SetWatchErrorHandler(_ cache.WatchErrorHandler) error { return nil } +func (mf *mockInformer) SetWatchErrorHandlerWithContext(_ cache.WatchErrorHandlerWithContext) error { + return nil +} + func (mf *mockInformer) SetTransform(_ cache.TransformFunc) error { return nil } diff --git a/pkg/controllers/proxy/network_services_controller.go b/pkg/controllers/proxy/network_services_controller.go index 1abbfe091..a773eb8dd 100644 --- a/pkg/controllers/proxy/network_services_controller.go +++ b/pkg/controllers/proxy/network_services_controller.go @@ -22,7 +22,7 @@ import ( "github.com/moby/ipvs" "github.com/vishvananda/netlink" v1 "k8s.io/api/core/v1" - discovery "k8s.io/api/discovery/v1" + discoveryv1 "k8s.io/api/discovery/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/leaderelection/resourcelock" @@ -807,7 +807,7 @@ func (nsc *NetworkServicesController) publishMetrics(serviceInfoMap serviceInfoM } // OnEndpointsUpdate handle change in endpoints update from the API server -func (nsc *NetworkServicesController) OnEndpointsUpdate(es *discovery.EndpointSlice) { +func (nsc *NetworkServicesController) OnEndpointsUpdate(es *discoveryv1.EndpointSlice) { nsc.mu.Lock() defer nsc.mu.Unlock() @@ -1057,14 +1057,14 @@ func (nsc *NetworkServicesController) buildEndpointSliceInfo() endpointSliceInfo endpointsMap := make(endpointSliceInfoMap) for _, obj := range nsc.epSliceLister.List() { var isIPv4, isIPv6 bool - es := obj.(*discovery.EndpointSlice) + es := obj.(*discoveryv1.EndpointSlice) klog.V(2).Infof("Building endpoint info for EndpointSlice: %s/%s", es.Namespace, es.Name) switch es.AddressType { - case discovery.AddressTypeIPv4: + case discoveryv1.AddressTypeIPv4: isIPv4 = true - case discovery.AddressTypeIPv6: + case discoveryv1.AddressTypeIPv6: isIPv6 = true - case discovery.AddressTypeFQDN: + case discoveryv1.AddressTypeFQDN: // At this point we don't handle FQDN type EndpointSlices, at some point in the future this might change continue default: @@ -1879,7 +1879,7 @@ func (nsc *NetworkServicesController) newEndpointSliceEventHandler() cache.Resou } func (nsc *NetworkServicesController) handleEndpointSliceAdd(obj interface{}) { - endpoints, ok := obj.(*discovery.EndpointSlice) + endpoints, ok := obj.(*discoveryv1.EndpointSlice) if !ok { klog.Errorf("unexpected object type: %v", obj) return @@ -1888,12 +1888,12 @@ func (nsc *NetworkServicesController) handleEndpointSliceAdd(obj interface{}) { } func (nsc *NetworkServicesController) handleEndpointSliceUpdate(oldObj, newObj interface{}) { - _, ok := oldObj.(*discovery.EndpointSlice) + _, ok := oldObj.(*discoveryv1.EndpointSlice) if !ok { klog.Errorf("unexpected object type: %v", oldObj) return } - newEndpoints, ok := newObj.(*discovery.EndpointSlice) + newEndpoints, ok := newObj.(*discoveryv1.EndpointSlice) if !ok { klog.Errorf("unexpected object type: %v", newObj) return @@ -1902,14 +1902,14 @@ func (nsc *NetworkServicesController) handleEndpointSliceUpdate(oldObj, newObj i } func (nsc *NetworkServicesController) handleEndpointSliceDelete(obj interface{}) { - endpoints, ok := obj.(*discovery.EndpointSlice) + endpoints, ok := obj.(*discoveryv1.EndpointSlice) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { klog.Errorf("unexpected object type: %v", obj) return } - if endpoints, ok = tombstone.Obj.(*discovery.EndpointSlice); !ok { + if endpoints, ok = tombstone.Obj.(*discoveryv1.EndpointSlice); !ok { klog.Errorf("unexpected object type: %v", obj) return } diff --git a/pkg/controllers/proxy/network_services_controller_test.go b/pkg/controllers/proxy/network_services_controller_test.go index 83d9f2d0b..ea581b3e9 100644 --- a/pkg/controllers/proxy/network_services_controller_test.go +++ b/pkg/controllers/proxy/network_services_controller_test.go @@ -6,12 +6,14 @@ import ( "net" "time" + "github.com/cloudnativelabs/kube-router/v2/pkg/k8s/indexers" "github.com/cloudnativelabs/kube-router/v2/pkg/utils" "github.com/moby/ipvs" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/vishvananda/netlink" v1core "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" @@ -104,7 +106,7 @@ func waitForListerWithTimeoutG(lister cache.Indexer, timeout time.Duration) { type TestCaseSvcEPs struct { existingService *v1core.Service - existingEndpoint *v1core.Endpoints + existingEndpoint *discoveryv1.EndpointSlice nodeHasEndpoints bool } @@ -131,7 +133,7 @@ var _ = Describe("NetworkServicesController", func() { JustBeforeEach(func() { clientset := fake.NewSimpleClientset() - _, err := clientset.CoreV1().Endpoints("default").Create(context.Background(), testcase.existingEndpoint, metav1.CreateOptions{}) + _, err := clientset.DiscoveryV1().EndpointSlices("default").Create(context.Background(), testcase.existingEndpoint, metav1.CreateOptions{}) if err != nil { fatalf("failed to create existing endpoints: %v", err) } @@ -175,7 +177,7 @@ var _ = Describe("NetworkServicesController", func() { }, }, }, - &v1core.Endpoints{}, + &discoveryv1.EndpointSlice{}, false, } }) @@ -262,7 +264,7 @@ var _ = Describe("NetworkServicesController", func() { }, }, }, - &v1core.Endpoints{}, + &discoveryv1.EndpointSlice{}, false, } }) @@ -329,7 +331,7 @@ var _ = Describe("NetworkServicesController", func() { }, }, }, - &v1core.Endpoints{}, + &discoveryv1.EndpointSlice{}, false, } }) @@ -390,7 +392,7 @@ var _ = Describe("NetworkServicesController", func() { }, }, }, - &v1core.Endpoints{}, + &discoveryv1.EndpointSlice{}, false, } }) @@ -442,21 +444,26 @@ var _ = Describe("NetworkServicesController", func() { }, }, }, - &v1core.Endpoints{ + &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - {IP: "172.20.1.1", NodeName: ptrToString("node-1")}, - {IP: "172.20.1.2", NodeName: ptrToString("node-2")}, - }, - Ports: []v1core.EndpointPort{ - {Name: "port-1", Port: 80, Protocol: "TCP"}, - }, + Addresses: []string{"172.20.1.1"}, + NodeName: stringToPtr("node-1"), }, + { + Addresses: []string{"172.20.1.2"}, + NodeName: stringToPtr("node-2"), + }, + }, + Ports: []discoveryv1.EndpointPort{ + {Name: stringToPtr("port-1"), Port: int32ToPtr(80), Protocol: protoToPtr(v1core.ProtocolTCP)}, }, }, true, @@ -502,17 +509,32 @@ var _ = Describe("NetworkServicesController", func() { func startInformersForServiceProxy(nsc *NetworkServicesController, clientset kubernetes.Interface) { informerFactory := informers.NewSharedInformerFactory(clientset, 0) svcInformer := informerFactory.Core().V1().Services().Informer() - epInformer := informerFactory.Core().V1().Endpoints().Informer() + epSliceInformer := informerFactory.Discovery().V1().EndpointSlices().Informer() podInformer := informerFactory.Core().V1().Pods().Informer() + err := epSliceInformer.AddIndexers(map[string]cache.IndexFunc{ + indexers.ServiceNameIndex: indexers.ServiceNameIndexFunc, + }) + if err != nil { + fatalf("failed to add indexers to endpoint slice informer: %v", err) + } + go informerFactory.Start(nil) informerFactory.WaitForCacheSync(nil) nsc.svcLister = svcInformer.GetIndexer() - nsc.epSliceLister = epInformer.GetIndexer() + nsc.epSliceLister = epSliceInformer.GetIndexer() nsc.podLister = podInformer.GetIndexer() } -func ptrToString(str string) *string { +func stringToPtr(str string) *string { return &str } + +func int32ToPtr(i int32) *int32 { + return &i +} + +func protoToPtr(proto v1core.Protocol) *v1core.Protocol { + return &proto +} diff --git a/pkg/controllers/routing/bgp_policies_test.go b/pkg/controllers/routing/bgp_policies_test.go index d74bed864..5e760c2e5 100644 --- a/pkg/controllers/routing/bgp_policies_test.go +++ b/pkg/controllers/routing/bgp_policies_test.go @@ -9,6 +9,7 @@ import ( "time" v1core "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" @@ -24,7 +25,7 @@ type PolicyTestCase struct { nrc *NetworkRoutingController existingNodes []*v1core.Node existingServices []*v1core.Service - existingEndpoints []*v1core.Endpoints + existingEndpoints []*discoveryv1.EndpointSlice podDefinedSet *gobgpapi.DefinedSet clusterIPDefinedSet *gobgpapi.DefinedSet externalPeerDefinedSet *gobgpapi.DefinedSet @@ -95,19 +96,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -244,19 +244,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -486,19 +485,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -690,19 +688,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -879,19 +876,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1088,19 +1084,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1298,19 +1293,18 @@ func Test_AddPolicies(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1453,7 +1447,7 @@ func Test_AddPolicies(t *testing.T) { t.Errorf("failed to create existing services: %v", err) } - if err := createEndpoints(testcase.nrc.clientset, testcase.existingEndpoints); err != nil { + if err := createEndpointSlices(testcase.nrc.clientset, testcase.existingEndpoints); err != nil { t.Errorf("failed to create existing endpoints: %v", err) } diff --git a/pkg/controllers/routing/ecmp_vip.go b/pkg/controllers/routing/ecmp_vip.go index 2b9b72dcc..f6f723e83 100644 --- a/pkg/controllers/routing/ecmp_vip.go +++ b/pkg/controllers/routing/ecmp_vip.go @@ -8,11 +8,13 @@ import ( "google.golang.org/protobuf/types/known/anypb" + "github.com/cloudnativelabs/kube-router/v2/pkg/k8s/indexers" "github.com/cloudnativelabs/kube-router/v2/pkg/metrics" "github.com/cloudnativelabs/kube-router/v2/pkg/utils" gobgpapi "github.com/osrg/gobgp/v3/api" v1core "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/leaderelection/resourcelock" "k8s.io/klog/v2" @@ -256,7 +258,7 @@ func (nrc *NetworkRoutingController) OnServiceDelete(oldObj interface{}) { nrc.tryHandleServiceDelete(oldObj, "Received event to delete service: %s/%s from watch API") } -func (nrc *NetworkRoutingController) newEndpointsEventHandler() cache.ResourceEventHandler { +func (nrc *NetworkRoutingController) newEndpointSliceEventHandler() cache.ResourceEventHandler { return cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { nrc.OnEndpointsAdd(obj) @@ -287,30 +289,30 @@ func (nrc *NetworkRoutingController) OnEndpointsAdd(obj interface{}) { // OnEndpointsUpdate handles the endpoint updates from the kubernetes API server func (nrc *NetworkRoutingController) OnEndpointsUpdate(obj interface{}) { - ep, ok := obj.(*v1core.Endpoints) + ep, ok := obj.(*discoveryv1.EndpointSlice) if !ok { - klog.Errorf("cache indexer returned obj that is not type *v1.Endpoints") + klog.Errorf("cache indexer returned obj that is not type *discoveryv1.EndpointSlice") return } - if isEndpointsForLeaderElection(ep) { + if isEndpointSliceForLeaderElection(ep) { return } - klog.V(1).Infof("Received update to endpoint: %s/%s from watch API", ep.Namespace, ep.Name) + klog.V(1).Infof("Received update to endpoint slice: %s/%s from watch API", ep.Namespace, ep.Name) if !nrc.bgpServerStarted { - klog.V(3).Infof("Skipping update to endpoint: %s/%s, controller still performing bootup full-sync", + klog.V(3).Infof("Skipping update to endpoint slice: %s/%s, controller still performing bootup full-sync", ep.Namespace, ep.Name) return } - svc, exists, err := utils.ServiceForEndpoints(&nrc.svcLister, ep) + svc, exists, err := utils.ServiceForEndpointSlice(&nrc.svcLister, ep) if err != nil { - klog.Errorf("failed to convert endpoints resource to service: %s", err) + klog.Errorf("failed to convert endpoint slice resource to service: %s", err) return } - // ignore updates to Endpoints object with no corresponding Service object + // ignore updates to EndpointSlice object with no corresponding Service object if !exists { return } @@ -564,44 +566,44 @@ func (nrc *NetworkRoutingController) getAllVIPsForService(svc *v1core.Service) ( } -func isEndpointsForLeaderElection(ep *v1core.Endpoints) bool { +func isEndpointSliceForLeaderElection(ep *discoveryv1.EndpointSlice) bool { _, isLeaderElection := ep.Annotations[resourcelock.LeaderElectionRecordAnnotationKey] return isLeaderElection } -// nodeHasEndpointsForService will get the corresponding Endpoints resource for a given Service +// nodeHasEndpointsForService will get the corresponding EndpointSlice resource for a given Service // return true if any endpoint addresses has NodeName matching the node name of the route controller func (nrc *NetworkRoutingController) nodeHasEndpointsForService(svc *v1core.Service) (bool, error) { - // listers for endpoints and services should use the same keys since - // endpoint and service resources share the same object name and namespace - key, err := cache.MetaNamespaceKeyFunc(svc) - if err != nil { - return false, err - } - item, exists, err := nrc.epLister.GetByKey(key) + // 1. Construct the lookup key from the Service object. + // This key must match the format produced by ServiceNameIndexFunc (see k8s/indexers/endpointslices.go) + key := fmt.Sprintf("%s/%s", svc.Namespace, svc.Name) + + slices, err := nrc.epSliceLister.ByIndex(indexers.ServiceNameIndex, key) if err != nil { return false, err } - if !exists { - return false, fmt.Errorf("endpoint resource doesn't exist for service: %q", svc.Name) + if len(slices) == 0 { + return false, fmt.Errorf("endpoint slice resource doesn't exist for service: %q", svc.Name) } - ep, ok := item.(*v1core.Endpoints) - if !ok { - return false, errors.New("failed to convert cache item to Endpoints type") - } + for _, slice := range slices { + es, ok := slice.(*discoveryv1.EndpointSlice) + if !ok { + return false, errors.New("failed to convert cache item to EndpointSlice type") + } - for _, subset := range ep.Subsets { - for _, address := range subset.Addresses { - if address.NodeName != nil { - if *address.NodeName == nrc.krNode.GetNodeName() { + for _, endpoint := range es.Endpoints { + if endpoint.NodeName != nil { + if *endpoint.NodeName == nrc.krNode.GetNodeName() { return true, nil } } else { - for _, nodeIP := range nrc.krNode.GetNodeIPAddrs() { - if address.IP == nodeIP.String() { - return true, nil + for _, address := range endpoint.Addresses { + for _, nodeIP := range nrc.krNode.GetNodeIPAddrs() { + if address == nodeIP.String() { + return true, nil + } } } } diff --git a/pkg/controllers/routing/ecmp_vip_test.go b/pkg/controllers/routing/ecmp_vip_test.go index 250040c3d..e849629e3 100644 --- a/pkg/controllers/routing/ecmp_vip_test.go +++ b/pkg/controllers/routing/ecmp_vip_test.go @@ -8,6 +8,7 @@ import ( "github.com/cloudnativelabs/kube-router/v2/pkg/utils" v1core "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" ) @@ -36,7 +37,7 @@ var ( type ServiceAdvertisedIPs struct { service *v1core.Service - endpoints *v1core.Endpoints + endpoints *discoveryv1.EndpointSlice internalTrafficPolicy *v1core.ServiceInternalTrafficPolicyType externalTrafficPolicy *v1core.ServiceExternalTrafficPolicyType advertisedIPs []string @@ -922,15 +923,18 @@ func Test_getVIPsForService(t *testing.T) { serviceAdvertisedIP.service.Spec.ExternalTrafficPolicy = *serviceAdvertisedIP.externalTrafficPolicy } - // Take care of adding endpoints if needed for test + // Take care of adding endpoint slices if needed for test if endpoints != nil { endpoints.Name = serviceAdvertisedIP.service.Name endpoints.Namespace = serviceAdvertisedIP.service.Namespace - if _, err := clientset.CoreV1().Endpoints(endpoints.GetObjectMeta().GetNamespace()).Create( + endpoints.Labels = map[string]string{ + "kubernetes.io/service-name": serviceAdvertisedIP.service.Name, + } + if _, err := clientset.DiscoveryV1().EndpointSlices(endpoints.GetObjectMeta().GetNamespace()).Create( context.Background(), endpoints, metav1.CreateOptions{}); err != nil { - t.Fatalf("failed to create endpoints for test: %v", err) + t.Fatalf("failed to create endpoint slice for test: %v", err) } - waitForListerWithTimeout(test.nrc.epLister, time.Second*10, t) + waitForListerWithTimeout(test.nrc.epSliceLister, time.Second*10, t) } svc, _ := clientset.CoreV1().Services("default").Create(context.Background(), serviceAdvertisedIP.service, metav1.CreateOptions{}) @@ -1026,80 +1030,43 @@ func getLoadBalancerSvc() *v1core.Service { } } -func getContainsLocalIPv4EPs() *v1core.Endpoints { - return &v1core.Endpoints{ - Subsets: []v1core.EndpointSubset{ +func getContainsLocalIPv4EPs() *discoveryv1.EndpointSlice { + return &discoveryv1.EndpointSlice{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - { - IP: "10.1.0.2", - }, - }, + Addresses: []string{testNodeIPv4, "10.1.0.2"}, }, { - Addresses: []v1core.EndpointAddress{ - { - IP: "10.1.1.1", - }, - }, + Addresses: []string{"10.1.1.1"}, }, }, } } -func getContainsLocalIPv6EPs() *v1core.Endpoints { - return &v1core.Endpoints{ - Subsets: []v1core.EndpointSubset{ +func getContainsLocalIPv6EPs() *discoveryv1.EndpointSlice { + return &discoveryv1.EndpointSlice{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv6, - }, - { - IP: "2001:db8:42:2::2", - }, - }, + Addresses: []string{testNodeIPv6, "2001:db8:42:2::2"}, }, { - Addresses: []v1core.EndpointAddress{ - { - IP: "2001:db8:42:2::3", - }, - }, + Addresses: []string{"2001:db8:42:2::3"}, }, }, } } -func getNoLocalAddressesEPs() *v1core.Endpoints { - return &v1core.Endpoints{ - Subsets: []v1core.EndpointSubset{ +func getNoLocalAddressesEPs() *discoveryv1.EndpointSlice { + return &discoveryv1.EndpointSlice{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: "2001:db8:42:2::3", - }, - { - IP: "2001:db8:42:2::2", - }, - }, + Addresses: []string{"2001:db8:42:2::3", "2001:db8:42:2::2"}, }, { - Addresses: []v1core.EndpointAddress{ - { - IP: "2001:db8:42:2::3", - }, - }, + Addresses: []string{"2001:db8:42:2::3"}, }, { - Addresses: []v1core.EndpointAddress{ - { - IP: "10.1.0.2", - }, - }, + Addresses: []string{"10.1.0.2"}, }, }, } diff --git a/pkg/controllers/routing/network_routes_controller.go b/pkg/controllers/routing/network_routes_controller.go index f0edebfd9..08d0fca4a 100644 --- a/pkg/controllers/routing/network_routes_controller.go +++ b/pkg/controllers/routing/network_routes_controller.go @@ -144,13 +144,13 @@ type NetworkRoutingController struct { pbr PolicyBasedRouter tunneler tunnels.Tunneler - nodeLister cache.Indexer - svcLister cache.Indexer - epLister cache.Indexer + nodeLister cache.Indexer + svcLister cache.Indexer + epSliceLister cache.Indexer - NodeEventHandler cache.ResourceEventHandler - ServiceEventHandler cache.ResourceEventHandler - EndpointsEventHandler cache.ResourceEventHandler + NodeEventHandler cache.ResourceEventHandler + ServiceEventHandler cache.ResourceEventHandler + EndpointSliceEventHandler cache.ResourceEventHandler } // Run runs forever until we are notified on stop channel @@ -1270,7 +1270,7 @@ func (nrc *NetworkRoutingController) setupHandlers(node *v1core.Node) error { func NewNetworkRoutingController(clientset kubernetes.Interface, kubeRouterConfig *options.KubeRouterConfig, nodeInformer cache.SharedIndexInformer, svcInformer cache.SharedIndexInformer, - epInformer cache.SharedIndexInformer, ipsetMutex *sync.Mutex) (*NetworkRoutingController, error) { + epSliceInformer cache.SharedIndexInformer, ipsetMutex *sync.Mutex) (*NetworkRoutingController, error) { var err error @@ -1488,8 +1488,8 @@ func NewNetworkRoutingController(clientset kubernetes.Interface, nrc.svcLister = svcInformer.GetIndexer() nrc.ServiceEventHandler = nrc.newServiceEventHandler() - nrc.epLister = epInformer.GetIndexer() - nrc.EndpointsEventHandler = nrc.newEndpointsEventHandler() + nrc.epSliceLister = epSliceInformer.GetIndexer() + nrc.EndpointSliceEventHandler = nrc.newEndpointSliceEventHandler() nrc.nodeLister = nodeInformer.GetIndexer() nrc.NodeEventHandler = nrc.newNodeEventHandler() diff --git a/pkg/controllers/routing/network_routes_controller_test.go b/pkg/controllers/routing/network_routes_controller_test.go index c5e063c46..66ac25756 100644 --- a/pkg/controllers/routing/network_routes_controller_test.go +++ b/pkg/controllers/routing/network_routes_controller_test.go @@ -10,8 +10,11 @@ import ( "testing" "time" + "github.com/cloudnativelabs/kube-router/v2/pkg/k8s/indexers" "github.com/cloudnativelabs/kube-router/v2/pkg/utils" + . "github.com/onsi/ginkgo" v1core "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" @@ -27,7 +30,7 @@ func Test_advertiseClusterIPs(t *testing.T) { name string nrc *NetworkRoutingController existingServices []*v1core.Service - existingEndpoints []*v1core.Endpoints + existingEndpoints []*discoveryv1.EndpointSlice // the key is the subnet from the watch event watchEvents map[string]bool }{ @@ -57,19 +60,18 @@ func Test_advertiseClusterIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -127,19 +129,18 @@ func Test_advertiseClusterIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -147,14 +148,13 @@ func Test_advertiseClusterIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -162,14 +162,13 @@ func Test_advertiseClusterIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -216,19 +215,18 @@ func Test_advertiseClusterIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -236,14 +234,13 @@ func Test_advertiseClusterIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -301,19 +298,18 @@ func Test_advertiseClusterIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -321,14 +317,13 @@ func Test_advertiseClusterIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -336,14 +331,13 @@ func Test_advertiseClusterIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -380,9 +374,9 @@ func Test_advertiseClusterIPs(t *testing.T) { t.Fatalf("failed to create existing services: %v", err) } - err = createEndpoints(clientset, testcase.existingEndpoints) + err = createEndpointSlices(clientset, testcase.existingEndpoints) if err != nil { - t.Fatalf("failed to create existing endpoints: %v", err) + t.Fatalf("failed to create existing endpoint slices: %v", err) } waitForListerWithTimeout(testcase.nrc.svcLister, time.Second*10, t) @@ -465,7 +459,7 @@ func Test_advertiseExternalIPs(t *testing.T) { name string nrc *NetworkRoutingController existingServices []*v1core.Service - existingEndpoints []*v1core.Endpoints + existingEndpoints []*discoveryv1.EndpointSlice // the key is the subnet from the watch event watchEvents map[string]bool }{ @@ -495,19 +489,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -569,19 +562,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -589,14 +581,13 @@ func Test_advertiseExternalIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -604,14 +595,13 @@ func Test_advertiseExternalIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -662,19 +652,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -682,14 +671,13 @@ func Test_advertiseExternalIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -750,19 +738,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -770,14 +757,13 @@ func Test_advertiseExternalIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -785,14 +771,13 @@ func Test_advertiseExternalIPs(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -835,19 +820,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -891,19 +875,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -943,19 +926,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1002,19 +984,18 @@ func Test_advertiseExternalIPs(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1076,9 +1057,9 @@ func Test_advertiseExternalIPs(t *testing.T) { t.Fatalf("failed to create existing services: %v", err) } - err = createEndpoints(clientset, testcase.existingEndpoints) + err = createEndpointSlices(clientset, testcase.existingEndpoints) if err != nil { - t.Fatalf("failed to create existing endpoints: %v", err) + t.Fatalf("failed to create existing endpoint slices: %v", err) } waitForListerWithTimeout(testcase.nrc.svcLister, time.Second*10, t) @@ -1134,7 +1115,7 @@ func Test_advertiseAnnotationOptOut(t *testing.T) { name string nrc *NetworkRoutingController existingServices []*v1core.Service - existingEndpoints []*v1core.Endpoints + existingEndpoints []*discoveryv1.EndpointSlice // the key is the subnet from the watch event watchEvents map[string]bool }{ @@ -1202,19 +1183,18 @@ func Test_advertiseAnnotationOptOut(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1222,14 +1202,13 @@ func Test_advertiseAnnotationOptOut(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1237,14 +1216,13 @@ func Test_advertiseAnnotationOptOut(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1304,19 +1282,18 @@ func Test_advertiseAnnotationOptOut(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1379,7 +1356,7 @@ func Test_advertiseAnnotationOptOut(t *testing.T) { t.Fatalf("failed to create existing services: %v", err) } - err = createEndpoints(clientset, testcase.existingEndpoints) + err = createEndpointSlices(clientset, testcase.existingEndpoints) if err != nil { t.Fatalf("failed to create existing endpoints: %v", err) } @@ -1437,7 +1414,7 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { name string nrc *NetworkRoutingController existingServices []*v1core.Service - existingEndpoints []*v1core.Endpoints + existingEndpoints []*discoveryv1.EndpointSlice // the key is the subnet from the watch event watchEvents map[string]bool }{ @@ -1507,19 +1484,18 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1527,14 +1503,13 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1542,14 +1517,13 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1635,19 +1609,18 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { }, }, }, - []*v1core.Endpoints{ + []*discoveryv1.EndpointSlice{ { ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1655,14 +1628,13 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-2", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-2", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1670,14 +1642,13 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "svc-3", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-3", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: testNodeIPv4, - }, - }, + Addresses: []string{testNodeIPv4}, }, }, }, @@ -1750,9 +1721,9 @@ func Test_advertiseAnnotationOptIn(t *testing.T) { t.Fatalf("failed to create existing services: %v", err) } - err = createEndpoints(clientset, testcase.existingEndpoints) + err = createEndpointSlices(clientset, testcase.existingEndpoints) if err != nil { - t.Fatalf("failed to create existing endpoints: %v", err) + t.Fatalf("failed to create existing endpoint slices: %v", err) } waitForListerWithTimeout(testcase.nrc.svcLister, time.Second*10, t) @@ -1809,7 +1780,7 @@ func Test_nodeHasEndpointsForService(t *testing.T) { name string nrc *NetworkRoutingController existingService *v1core.Service - existingEndpoint *v1core.Endpoints + existingEndpoint *discoveryv1.EndpointSlice nodeHasEndpoints bool }{ { @@ -1836,23 +1807,22 @@ func Test_nodeHasEndpointsForService(t *testing.T) { ExternalTrafficPolicy: testClusterExtTrafPol, }, }, - &v1core.Endpoints{ + &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: "172.20.1.1", - NodeName: ptrToString("node-1"), - }, - { - IP: "172.20.1.2", - NodeName: ptrToString("node-2"), - }, - }, + Addresses: []string{"172.20.1.1"}, + NodeName: ptrToString("node-1"), + }, + { + Addresses: []string{"172.20.1.2"}, + NodeName: ptrToString("node-2"), }, }, }, @@ -1882,23 +1852,22 @@ func Test_nodeHasEndpointsForService(t *testing.T) { ExternalTrafficPolicy: testClusterExtTrafPol, }, }, - &v1core.Endpoints{ + &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Name: "svc-1", Namespace: "default", + Labels: map[string]string{ + "kubernetes.io/service-name": "svc-1", + }, }, - Subsets: []v1core.EndpointSubset{ + Endpoints: []discoveryv1.Endpoint{ { - Addresses: []v1core.EndpointAddress{ - { - IP: "172.20.1.1", - NodeName: ptrToString("node-2"), - }, - { - IP: "172.20.1.2", - NodeName: ptrToString("node-3"), - }, - }, + Addresses: []string{"172.20.1.1"}, + NodeName: ptrToString("node-2"), + }, + { + Addresses: []string{"172.20.1.2"}, + NodeName: ptrToString("node-3"), }, }, }, @@ -1911,7 +1880,7 @@ func Test_nodeHasEndpointsForService(t *testing.T) { clientset := fake.NewSimpleClientset() startInformersForRoutes(testcase.nrc, clientset) - _, err := clientset.CoreV1().Endpoints("default").Create(context.Background(), testcase.existingEndpoint, metav1.CreateOptions{}) + _, err := clientset.DiscoveryV1().EndpointSlices("default").Create(context.Background(), testcase.existingEndpoint, metav1.CreateOptions{}) if err != nil { t.Fatalf("failed to create existing endpoints: %v", err) } @@ -1922,7 +1891,7 @@ func Test_nodeHasEndpointsForService(t *testing.T) { } waitForListerWithTimeout(testcase.nrc.svcLister, time.Second*10, t) - waitForListerWithTimeout(testcase.nrc.epLister, time.Second*10, t) + waitForListerWithTimeout(testcase.nrc.epSliceLister, time.Second*10, t) nodeHasEndpoints, err := testcase.nrc.nodeHasEndpointsForService(testcase.existingService) if err != nil { @@ -2843,10 +2812,10 @@ func createNodes(clientset kubernetes.Interface, nodes []*v1core.Node) error { return nil } -func createEndpoints(clientset kubernetes.Interface, endpoints []*v1core.Endpoints) error { - for _, eps := range endpoints { - _, err := clientset.CoreV1().Endpoints(eps.ObjectMeta.Namespace).Create( - context.Background(), eps, metav1.CreateOptions{}) +func createEndpointSlices(clientset kubernetes.Interface, endpointSlices []*discoveryv1.EndpointSlice) error { + for _, es := range endpointSlices { + _, err := clientset.DiscoveryV1().EndpointSlices(es.ObjectMeta.Namespace).Create( + context.Background(), es, metav1.CreateOptions{}) if err != nil { return err } @@ -2855,17 +2824,29 @@ func createEndpoints(clientset kubernetes.Interface, endpoints []*v1core.Endpoin return nil } +func fatalf(format string, a ...interface{}) { + msg := fmt.Sprintf("FATAL: "+format+"\n", a...) + Fail(msg) +} + func startInformersForRoutes(nrc *NetworkRoutingController, clientset kubernetes.Interface) { informerFactory := informers.NewSharedInformerFactory(clientset, 0) svcInformer := informerFactory.Core().V1().Services().Informer() - epInformer := informerFactory.Core().V1().Endpoints().Informer() + epSliceInformer := informerFactory.Discovery().V1().EndpointSlices().Informer() nodeInformer := informerFactory.Core().V1().Nodes().Informer() + err := epSliceInformer.AddIndexers(map[string]cache.IndexFunc{ + indexers.ServiceNameIndex: indexers.ServiceNameIndexFunc, + }) + if err != nil { + fatalf("failed to add indexers to endpoint slice informer: %v", err) + } + go informerFactory.Start(nil) informerFactory.WaitForCacheSync(nil) nrc.svcLister = svcInformer.GetIndexer() - nrc.epLister = epInformer.GetIndexer() + nrc.epSliceLister = epSliceInformer.GetIndexer() nrc.nodeLister = nodeInformer.GetIndexer() } diff --git a/pkg/k8s/indexers/endpointslices.go b/pkg/k8s/indexers/endpointslices.go new file mode 100644 index 000000000..f414e9a42 --- /dev/null +++ b/pkg/k8s/indexers/endpointslices.go @@ -0,0 +1,27 @@ +package indexers + +import ( + "fmt" + + discoveryv1 "k8s.io/api/discovery/v1" +) + +// ServiceNameIndex is the name for our custom index. +const ServiceNameIndex = "service-name" + +// ServiceNameIndexFunc creates an index key based on an EndpointSlice's parent Service. +// The key is in the format "/". +func ServiceNameIndexFunc(obj interface{}) ([]string, error) { + slice, ok := obj.(*discoveryv1.EndpointSlice) + if !ok { + return []string{}, nil + } + + serviceName, ok := slice.Labels[discoveryv1.LabelServiceName] + if !ok || serviceName == "" { + // This slice is not associated with a Service, so we can't index it. + return []string{}, nil + } + + return []string{fmt.Sprintf("%s/%s", slice.Namespace, serviceName)}, nil +} diff --git a/pkg/utils/service.go b/pkg/utils/service.go index a266093c3..985986fb5 100644 --- a/pkg/utils/service.go +++ b/pkg/utils/service.go @@ -5,7 +5,7 @@ import ( "strings" v1core "k8s.io/api/core/v1" - discovery "k8s.io/api/discovery/v1" + discoveryv1 "k8s.io/api/discovery/v1" "k8s.io/client-go/tools/cache" "k8s.io/klog/v2" ) @@ -14,26 +14,6 @@ const ( IPInIPHeaderLength = 20 ) -// ServiceForEndpoints given Endpoint object return Service API object if it exists -func ServiceForEndpoints(ci *cache.Indexer, ep *v1core.Endpoints) (interface{}, bool, error) { - key, err := cache.MetaNamespaceKeyFunc(ep) - if err != nil { - return nil, false, err - } - klog.V(2).Infof("key for looking up service from Endpoint is: %s", key) - - item, exists, err := (*ci).GetByKey(key) - if err != nil { - return nil, false, err - } - - if !exists { - return nil, false, nil - } - - return item, true, nil -} - // ServiceNameforEndpointSlice returns the name of the service that created the EndpointSlice for a given EndpointSlice // // With endpoints, the name of the endpoint object always matches the service object, however when it comes to @@ -46,7 +26,7 @@ func ServiceForEndpoints(ci *cache.Indexer, ep *v1core.Endpoints) (interface{}, // // We'll all through all of these and do our best to identify the service's name, if we aren't able to find any of these // or they disagree with each other we'll throw an error -func ServiceNameforEndpointSlice(es *discovery.EndpointSlice) (string, error) { +func ServiceNameforEndpointSlice(es *discoveryv1.EndpointSlice) (string, error) { const serviceNameLabel = "kubernetes.io/service-name" var ownerRefName, labelSvcName, generateName, finalSvcName string @@ -93,7 +73,7 @@ func ServiceNameforEndpointSlice(es *discovery.EndpointSlice) (string, error) { } // ServiceForEndpoints given EndpointSlice object return Service API object if it exists -func ServiceForEndpointSlice(ci *cache.Indexer, es *discovery.EndpointSlice) (interface{}, bool, error) { +func ServiceForEndpointSlice(ci *cache.Indexer, es *discoveryv1.EndpointSlice) (interface{}, bool, error) { svcName, err := ServiceNameforEndpointSlice(es) if err != nil { return nil, false, err