github.com/cilium/cilium@v1.16.2/pkg/service/service_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package service 5 6 import ( 7 "errors" 8 "net" 9 "net/netip" 10 "syscall" 11 "testing" 12 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 "github.com/vishvananda/netlink" 16 "golang.org/x/exp/maps" 17 "golang.org/x/sys/unix" 18 "k8s.io/apimachinery/pkg/util/sets" 19 20 "github.com/cilium/cilium/api/v1/models" 21 "github.com/cilium/cilium/pkg/cidr" 22 cmtypes "github.com/cilium/cilium/pkg/clustermesh/types" 23 datapathOpt "github.com/cilium/cilium/pkg/datapath/option" 24 datapathTypes "github.com/cilium/cilium/pkg/datapath/types" 25 "github.com/cilium/cilium/pkg/k8s" 26 lb "github.com/cilium/cilium/pkg/loadbalancer" 27 "github.com/cilium/cilium/pkg/maps/lbmap" 28 monitorAgent "github.com/cilium/cilium/pkg/monitor/agent" 29 "github.com/cilium/cilium/pkg/monitor/agent/consumer" 30 "github.com/cilium/cilium/pkg/monitor/agent/listener" 31 nodeTypes "github.com/cilium/cilium/pkg/node/types" 32 "github.com/cilium/cilium/pkg/option" 33 "github.com/cilium/cilium/pkg/service/healthserver" 34 "github.com/cilium/cilium/pkg/testutils/mockmaps" 35 testsockets "github.com/cilium/cilium/pkg/testutils/sockets" 36 ) 37 38 func TestLocalRedirectServiceExistsError(t *testing.T) { 39 addrCluster1 := cmtypes.MustParseAddrCluster("1.2.3.4") 40 addrCluster2 := cmtypes.MustParseAddrCluster("5.6.7.8") 41 name1 := "my-svc-1" 42 name2 := "my-svc-2" 43 44 // same frontend, same name 45 err1 := NewErrLocalRedirectServiceExists( 46 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 47 lb.ServiceName{Namespace: "default", Name: name1}, 48 ) 49 err2 := NewErrLocalRedirectServiceExists( 50 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 51 lb.ServiceName{Namespace: "default", Name: name1}, 52 ) 53 assert.Equal(t, err1, err2) 54 assert.True(t, errors.Is(err1, err2)) 55 56 // same frontend, different name 57 err1 = NewErrLocalRedirectServiceExists( 58 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 59 lb.ServiceName{Namespace: "default", Name: name1}, 60 ) 61 err2 = NewErrLocalRedirectServiceExists( 62 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 63 lb.ServiceName{Namespace: "default", Name: name2}, 64 ) 65 assert.NotEqual(t, err1, err2) 66 assert.False(t, errors.Is(err1, err2)) 67 68 // different frontend, same name 69 err1 = NewErrLocalRedirectServiceExists( 70 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 71 lb.ServiceName{Namespace: "default", Name: name1}, 72 ) 73 err2 = NewErrLocalRedirectServiceExists( 74 *lb.NewL3n4AddrID(lb.TCP, addrCluster2, 8080, lb.ScopeInternal, 1), 75 lb.ServiceName{Namespace: "default", Name: name1}, 76 ) 77 assert.NotEqual(t, err1, err2) 78 assert.False(t, errors.Is(err1, err2)) 79 80 // different frontend, different name 81 err1 = NewErrLocalRedirectServiceExists( 82 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 83 lb.ServiceName{Namespace: "default", Name: name1}, 84 ) 85 err2 = NewErrLocalRedirectServiceExists( 86 *lb.NewL3n4AddrID(lb.TCP, addrCluster2, 8080, lb.ScopeInternal, 1), 87 lb.ServiceName{Namespace: "default", Name: name2}, 88 ) 89 assert.NotEqual(t, err1, err2) 90 assert.False(t, errors.Is(err1, err2)) 91 92 // different error types 93 err1 = NewErrLocalRedirectServiceExists( 94 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 95 lb.ServiceName{Namespace: "default", Name: name1}, 96 ) 97 err2 = errors.New("another error") 98 assert.NotEqual(t, err1, err2) 99 assert.False(t, errors.Is(err1, err2)) 100 101 // different error types 102 err1 = errors.New("another error") 103 err2 = NewErrLocalRedirectServiceExists( 104 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 105 lb.ServiceName{Namespace: "default", Name: name1}, 106 ) 107 assert.NotEqual(t, err1, err2) 108 assert.False(t, errors.Is(err1, err2)) 109 110 // an error is nil 111 err1 = NewErrLocalRedirectServiceExists( 112 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 113 lb.ServiceName{Namespace: "default", Name: name1}, 114 ) 115 err2 = nil 116 assert.NotEqual(t, err1, err2) 117 assert.False(t, errors.Is(err1, err2)) 118 119 // an error is nil 120 err1 = nil 121 err2 = NewErrLocalRedirectServiceExists( 122 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 123 lb.ServiceName{Namespace: "default", Name: name1}, 124 ) 125 assert.NotEqual(t, err1, err2) 126 assert.False(t, errors.Is(err1, err2)) 127 128 // We don't match against strings. It must be the sentinel value. 129 err1 = NewErrLocalRedirectServiceExists( 130 *lb.NewL3n4AddrID(lb.TCP, addrCluster1, 8080, lb.ScopeInternal, 1), 131 lb.ServiceName{Namespace: "default", Name: name1}, 132 ) 133 err2 = errors.New(err1.Error()) 134 assert.NotEqual(t, err1, err2) 135 assert.False(t, errors.Is(err1, err2)) 136 } 137 138 type ManagerTestSuite struct { 139 svc *Service 140 lbmap *mockmaps.LBMockMap // for accessing public fields 141 svcHealth *healthserver.MockHealthHTTPServerFactory 142 prevOptionSessionAffinity bool 143 prevOptionLBSourceRanges bool 144 prevOptionNPAlgo string 145 prevOptionDPMode string 146 prevOptionExternalClusterIP bool 147 ipv6 bool 148 } 149 150 var ( 151 surrogateFE = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("0.0.0.0"), 80, lb.ScopeExternal, 0) 152 surrogateFEv6 = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("::"), 80, lb.ScopeExternal, 0) 153 frontend1 = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.1"), 80, lb.ScopeExternal, 0) 154 frontend1_8080 = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.1"), 8080, lb.ScopeExternal, 0) 155 frontend2 = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.2"), 80, lb.ScopeExternal, 0) 156 frontend3 = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("f00d::1"), 80, lb.ScopeExternal, 0) 157 158 backends1, backends2, backends3, backends4, backends5, backends6 []*lb.Backend 159 ) 160 161 func setupManagerTestSuite(tb testing.TB) *ManagerTestSuite { 162 m := &ManagerTestSuite{} 163 serviceIDAlloc.resetLocalID() 164 backendIDAlloc.resetLocalID() 165 166 m.lbmap = mockmaps.NewLBMockMap() 167 m.newServiceMock(m.lbmap) 168 169 m.svcHealth = healthserver.NewMockHealthHTTPServerFactory() 170 m.svc.healthServer = healthserver.WithHealthHTTPServerFactory(m.svcHealth) 171 172 m.prevOptionSessionAffinity = option.Config.EnableSessionAffinity 173 option.Config.EnableSessionAffinity = true 174 175 m.prevOptionLBSourceRanges = option.Config.EnableSVCSourceRangeCheck 176 option.Config.EnableSVCSourceRangeCheck = true 177 178 m.prevOptionNPAlgo = option.Config.NodePortAlg 179 m.prevOptionDPMode = option.Config.DatapathMode 180 m.prevOptionExternalClusterIP = option.Config.ExternalClusterIP 181 182 m.ipv6 = option.Config.EnableIPv6 183 backends1 = []*lb.Backend{ 184 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.1"), 8080), 185 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.2"), 8080), 186 } 187 backends2 = []*lb.Backend{ 188 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.2"), 8080), 189 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.3"), 8080), 190 } 191 backends3 = []*lb.Backend{ 192 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("fd00::2"), 8080), 193 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("fd00::3"), 8080), 194 } 195 backends4 = []*lb.Backend{ 196 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.4"), 8080), 197 } 198 backends5 = []*lb.Backend{ 199 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.5"), 8080), 200 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.6"), 8080), 201 } 202 backends6 = []*lb.Backend{ 203 lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.7"), 8080), 204 } 205 206 tb.Cleanup(func() { 207 serviceIDAlloc.resetLocalID() 208 backendIDAlloc.resetLocalID() 209 option.Config.EnableSessionAffinity = m.prevOptionSessionAffinity 210 option.Config.EnableSVCSourceRangeCheck = m.prevOptionLBSourceRanges 211 option.Config.NodePortAlg = m.prevOptionNPAlgo 212 option.Config.DatapathMode = m.prevOptionDPMode 213 option.Config.ExternalClusterIP = m.prevOptionExternalClusterIP 214 option.Config.EnableIPv6 = m.ipv6 215 }) 216 217 return m 218 } 219 220 func (m *ManagerTestSuite) newServiceMock(lbmap datapathTypes.LBMap) { 221 m.svc = newService(&FakeMonitorAgent{}, lbmap, nil) 222 m.svc.backendConnectionHandler = testsockets.NewMockSockets(make([]*testsockets.MockSocket, 0)) 223 } 224 225 func TestUpsertAndDeleteService(t *testing.T) { 226 m := setupManagerTestSuite(t) 227 m.testUpsertAndDeleteService(t) 228 } 229 230 func TestUpsertAndDeleteServiceWithoutIPv6(t *testing.T) { 231 m := setupManagerTestSuite(t) 232 233 option.Config.EnableIPv6 = false 234 m.testUpsertAndDeleteService(t) 235 } 236 237 func TestUpsertAndDeleteServiceNat46(t *testing.T) { 238 m := setupManagerTestSuite(t) 239 240 option.Config.EnableIPv4 = true 241 option.Config.EnableIPv6 = true 242 option.Config.NodePortNat46X64 = true 243 m.testUpsertAndDeleteService46(t) 244 } 245 246 func TestUpsertAndDeleteServiceNat64(t *testing.T) { 247 m := setupManagerTestSuite(t) 248 249 option.Config.EnableIPv4 = true 250 option.Config.EnableIPv6 = true 251 option.Config.NodePortNat46X64 = true 252 m.testUpsertAndDeleteService64(t) 253 } 254 255 func (m *ManagerTestSuite) testUpsertAndDeleteService46(t *testing.T) { 256 // Should create a new v4 service with two v6 backends 257 p := &lb.SVC{ 258 Frontend: frontend1, 259 Backends: backends3, 260 Type: lb.SVCTypeNodePort, 261 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 262 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 263 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 264 } 265 created, id1, err := m.svc.UpsertService(p) 266 require.Nil(t, err) 267 require.Equal(t, true, created) 268 require.Equal(t, lb.ID(1), id1) 269 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 270 require.Equal(t, 2, len(m.lbmap.BackendByID)) 271 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 272 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 273 require.Equal(t, lb.SVCNatPolicyNat46, m.svc.svcByID[id1].svcNatPolicy) 274 275 // Should delete both backends of service 276 p.Backends = nil 277 created, id2, err := m.svc.UpsertService(p) 278 require.Nil(t, err) 279 require.Equal(t, false, created) 280 require.Equal(t, id1, id2) 281 require.Equal(t, 0, len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 282 require.Equal(t, 0, len(m.lbmap.BackendByID)) 283 require.Equal(t, "svc1", m.svc.svcByID[id2].svcName.Name) 284 require.Equal(t, "ns1", m.svc.svcByID[id2].svcName.Namespace) 285 require.Equal(t, lb.SVCNatPolicyNone, m.svc.svcByID[id2].svcNatPolicy) 286 287 // Should delete the remaining service 288 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 289 require.Nil(t, err) 290 require.Equal(t, true, found) 291 require.Equal(t, 0, len(m.lbmap.ServiceByID)) 292 require.Equal(t, 0, len(m.lbmap.BackendByID)) 293 } 294 295 func (m *ManagerTestSuite) testUpsertAndDeleteService64(t *testing.T) { 296 // Should create a new v6 service with two v4 backends 297 p := &lb.SVC{ 298 Frontend: frontend3, 299 Backends: backends1, 300 Type: lb.SVCTypeNodePort, 301 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 302 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 303 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 304 } 305 created, id1, err := m.svc.UpsertService(p) 306 require.Nil(t, err) 307 require.Equal(t, true, created) 308 require.Equal(t, lb.ID(1), id1) 309 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 310 require.Equal(t, 2, len(m.lbmap.BackendByID)) 311 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 312 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 313 require.Equal(t, lb.SVCNatPolicyNat64, m.svc.svcByID[id1].svcNatPolicy) 314 315 // Should delete both backends of service 316 p.Backends = nil 317 created, id2, err := m.svc.UpsertService(p) 318 require.Nil(t, err) 319 require.Equal(t, false, created) 320 require.Equal(t, id1, id2) 321 require.Equal(t, 0, len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 322 require.Equal(t, 0, len(m.lbmap.BackendByID)) 323 require.Equal(t, "svc1", m.svc.svcByID[id2].svcName.Name) 324 require.Equal(t, "ns1", m.svc.svcByID[id2].svcName.Namespace) 325 require.Equal(t, lb.SVCNatPolicyNone, m.svc.svcByID[id2].svcNatPolicy) 326 327 // Should delete the remaining service 328 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 329 require.Nil(t, err) 330 require.Equal(t, true, found) 331 require.Equal(t, 0, len(m.lbmap.ServiceByID)) 332 require.Equal(t, 0, len(m.lbmap.BackendByID)) 333 } 334 335 func (m *ManagerTestSuite) testUpsertAndDeleteService(t *testing.T) { 336 // Should create a new service with two backends and session affinity 337 p := &lb.SVC{ 338 Frontend: frontend1, 339 Backends: backends1, 340 Type: lb.SVCTypeNodePort, 341 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 342 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 343 SessionAffinity: true, 344 SessionAffinityTimeoutSec: 100, 345 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 346 } 347 created, id1, err := m.svc.UpsertService(p) 348 require.Nil(t, err) 349 require.Equal(t, true, created) 350 require.Equal(t, lb.ID(1), id1) 351 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 352 require.Equal(t, 2, len(m.lbmap.BackendByID)) 353 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 354 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 355 require.Equal(t, true, m.svc.svcByID[id1].sessionAffinity) 356 require.Equal(t, uint32(100), m.svc.svcByID[id1].sessionAffinityTimeoutSec) 357 require.Equal(t, true, m.lbmap.ServiceByID[uint16(id1)].SessionAffinity) 358 require.Equal(t, uint32(100), m.lbmap.ServiceByID[uint16(id1)].SessionAffinityTimeoutSec) 359 require.Equal(t, 2, len(m.lbmap.AffinityMatch[uint16(id1)])) 360 for bID := range m.lbmap.BackendByID { 361 require.Equal(t, struct{}{}, m.lbmap.AffinityMatch[uint16(id1)][bID]) 362 } 363 364 // Should remove session affinity 365 p.SessionAffinity = false 366 created, id1, err = m.svc.UpsertService(p) 367 require.Nil(t, err) 368 require.Equal(t, false, created) 369 require.Equal(t, lb.ID(1), id1) 370 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 371 require.Equal(t, 2, len(m.lbmap.BackendByID)) 372 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 373 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 374 require.Equal(t, 0, len(m.lbmap.AffinityMatch[uint16(id1)])) 375 require.Equal(t, false, m.svc.svcByID[id1].sessionAffinity) 376 require.Equal(t, false, m.lbmap.ServiceByID[uint16(id1)].SessionAffinity) 377 // TODO(brb) test that backends are the same 378 // TODO(brb) check that .backends =~ .backendsByHash 379 380 // Should remove one backend and enable session affinity 381 p.Backends = backends1[0:1] 382 p.SessionAffinity = true 383 p.SessionAffinityTimeoutSec = 200 384 created, id1, err = m.svc.UpsertService(p) 385 require.Nil(t, err) 386 require.Equal(t, false, created) 387 require.Equal(t, lb.ID(1), id1) 388 require.Equal(t, 1, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 389 require.Equal(t, 1, len(m.lbmap.BackendByID)) 390 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 391 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 392 require.Equal(t, true, m.svc.svcByID[id1].sessionAffinity) 393 require.Equal(t, uint32(200), m.svc.svcByID[id1].sessionAffinityTimeoutSec) 394 require.Equal(t, 1, len(m.lbmap.AffinityMatch[uint16(id1)])) 395 for bID := range m.lbmap.BackendByID { 396 require.Equal(t, struct{}{}, m.lbmap.AffinityMatch[uint16(id1)][bID]) 397 } 398 399 // Should add another service 400 require.Nil(t, err) 401 cidr1, err := cidr.ParseCIDR("10.0.0.0/8") 402 require.Nil(t, err) 403 cidr2, err := cidr.ParseCIDR("192.168.1.0/24") 404 require.Nil(t, err) 405 p2 := &lb.SVC{ 406 Frontend: frontend2, 407 Backends: backends1, 408 Type: lb.SVCTypeLoadBalancer, 409 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 410 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 411 SessionAffinity: true, 412 SessionAffinityTimeoutSec: 300, 413 Name: lb.ServiceName{Name: "svc2", Namespace: "ns2"}, 414 LoadBalancerSourceRanges: []*cidr.CIDR{cidr1, cidr2}, 415 } 416 created, id2, err := m.svc.UpsertService(p2) 417 require.Nil(t, err) 418 require.Equal(t, true, created) 419 require.Equal(t, lb.ID(2), id2) 420 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 421 require.Equal(t, 2, len(m.lbmap.BackendByID)) 422 require.Equal(t, "svc2", m.svc.svcByID[id2].svcName.Name) 423 require.Equal(t, "ns2", m.svc.svcByID[id2].svcName.Namespace) 424 require.Equal(t, 2, len(m.lbmap.AffinityMatch[uint16(id2)])) 425 require.Equal(t, 2, len(m.lbmap.SourceRanges[uint16(id2)])) 426 427 // Should add IPv6 service only if IPv6 is enabled 428 require.Nil(t, err) 429 cidr1, err = cidr.ParseCIDR("fd00::/8") 430 require.Nil(t, err) 431 p3 := &lb.SVC{ 432 Frontend: frontend3, 433 Backends: backends3, 434 Type: lb.SVCTypeLoadBalancer, 435 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 436 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 437 SessionAffinity: true, 438 SessionAffinityTimeoutSec: 300, 439 Name: lb.ServiceName{Name: "svc3", Namespace: "ns3"}, 440 LoadBalancerSourceRanges: []*cidr.CIDR{cidr1}, 441 } 442 created, id3, err := m.svc.UpsertService(p3) 443 if option.Config.EnableIPv6 { 444 require.Nil(t, err) 445 require.Equal(t, true, created) 446 require.Equal(t, lb.ID(3), id3) 447 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id3)].Backends)) 448 require.Equal(t, 4, len(m.lbmap.BackendByID)) 449 require.Equal(t, "svc3", m.svc.svcByID[id3].svcName.Name) 450 require.Equal(t, "ns3", m.svc.svcByID[id3].svcName.Namespace) 451 require.Equal(t, 2, len(m.lbmap.AffinityMatch[uint16(id3)])) 452 require.Equal(t, 1, len(m.lbmap.SourceRanges[uint16(id3)])) 453 454 // Should remove the IPv6 service 455 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id3)) 456 require.Nil(t, err) 457 require.Equal(t, true, found) 458 } else { 459 require.ErrorContains(t, err, "Unable to upsert service") 460 require.ErrorContains(t, err, "as IPv6 is disabled") 461 require.Equal(t, false, created) 462 } 463 require.Equal(t, 2, len(m.lbmap.ServiceByID)) 464 require.Equal(t, 2, len(m.lbmap.BackendByID)) 465 466 // Should remove the service and the backend, but keep another service and 467 // its backends. Also, should remove the affinity match. 468 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 469 require.Nil(t, err) 470 require.Equal(t, true, found) 471 require.Equal(t, 1, len(m.lbmap.ServiceByID)) 472 require.Equal(t, 2, len(m.lbmap.BackendByID)) 473 require.Equal(t, 0, len(m.lbmap.AffinityMatch[uint16(id1)])) 474 475 // Should delete both backends of service 476 p2.Backends = nil 477 p2.LoadBalancerSourceRanges = []*cidr.CIDR{cidr2} 478 created, id2, err = m.svc.UpsertService(p2) 479 require.Nil(t, err) 480 require.Equal(t, false, created) 481 require.Equal(t, lb.ID(2), id2) 482 require.Equal(t, 0, len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 483 require.Equal(t, 0, len(m.lbmap.BackendByID)) 484 require.Equal(t, "svc2", m.svc.svcByID[id2].svcName.Name) 485 require.Equal(t, "ns2", m.svc.svcByID[id2].svcName.Namespace) 486 require.Equal(t, 0, len(m.lbmap.AffinityMatch[uint16(id2)])) 487 require.Equal(t, 1, len(m.lbmap.SourceRanges[uint16(id2)])) 488 489 // Should delete the remaining service 490 found, err = m.svc.DeleteServiceByID(lb.ServiceID(id2)) 491 require.Nil(t, err) 492 require.Equal(t, true, found) 493 require.Equal(t, 0, len(m.lbmap.ServiceByID)) 494 require.Equal(t, 0, len(m.lbmap.BackendByID)) 495 496 // Should ignore the source range if it does not match FE's ip family 497 cidr1, err = cidr.ParseCIDR("fd00::/8") 498 require.Nil(t, err) 499 cidr2, err = cidr.ParseCIDR("192.168.1.0/24") 500 require.Nil(t, err) 501 502 p4 := &lb.SVC{ 503 Frontend: frontend1, 504 Backends: backends1, 505 Type: lb.SVCTypeLoadBalancer, 506 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 507 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 508 SessionAffinity: true, 509 SessionAffinityTimeoutSec: 300, 510 Name: lb.ServiceName{Name: "svc3", Namespace: "ns3"}, 511 LoadBalancerSourceRanges: []*cidr.CIDR{cidr1, cidr2}, 512 } 513 created, id4, err := m.svc.UpsertService(p4) 514 require.Equal(t, true, created) 515 require.Nil(t, err) 516 require.Equal(t, 1, len(m.lbmap.SourceRanges[uint16(id4)])) 517 } 518 519 func TestRestoreServices(t *testing.T) { 520 m := setupManagerTestSuite(t) 521 522 p1 := &lb.SVC{ 523 Frontend: frontend1, 524 Backends: backends1, 525 Type: lb.SVCTypeNodePort, 526 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 527 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 528 } 529 _, id1, err := m.svc.UpsertService(p1) 530 require.Nil(t, err) 531 cidr1, err := cidr.ParseCIDR("10.0.0.0/8") 532 require.Nil(t, err) 533 cidr2, err := cidr.ParseCIDR("192.168.1.0/24") 534 require.Nil(t, err) 535 p2 := &lb.SVC{ 536 Frontend: frontend2, 537 Backends: backends2, 538 Type: lb.SVCTypeLoadBalancer, 539 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 540 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 541 SessionAffinity: true, 542 SessionAffinityTimeoutSec: 200, 543 LoadBalancerSourceRanges: []*cidr.CIDR{cidr1, cidr2}, 544 } 545 _, id2, err := m.svc.UpsertService(p2) 546 require.Nil(t, err) 547 548 // Restart service, but keep the lbmap to restore services from 549 option.Config.NodePortAlg = option.NodePortAlgMaglev 550 option.Config.DatapathMode = datapathOpt.DatapathModeLBOnly 551 lbmap := m.svc.lbmap.(*mockmaps.LBMockMap) 552 m.newServiceMock(lbmap) 553 554 // Restore services from lbmap 555 err = m.svc.RestoreServices() 556 require.Nil(t, err) 557 558 // Backends have been restored 559 require.Equal(t, 3, len(m.svc.backendByHash)) 560 backends := append(backends1, backends2...) 561 for _, b := range backends { 562 _, found := m.svc.backendByHash[b.Hash()] 563 require.Equal(t, true, found) 564 } 565 566 // Services have been restored too 567 require.Equal(t, 2, len(m.svc.svcByID)) 568 require.EqualValues(t, lbmap.ServiceByID[uint16(id1)].Frontend, m.svc.svcByID[id1].frontend) 569 require.EqualValues(t, lbmap.ServiceByID[uint16(id1)].Backends, m.svc.svcByID[id1].backends) 570 require.EqualValues(t, lbmap.ServiceByID[uint16(id2)].Frontend, m.svc.svcByID[id2].frontend) 571 require.EqualValues(t, lbmap.ServiceByID[uint16(id2)].Backends, m.svc.svcByID[id2].backends) 572 573 // Session affinity too 574 require.Equal(t, false, m.svc.svcByID[id1].sessionAffinity) 575 require.Equal(t, true, m.svc.svcByID[id2].sessionAffinity) 576 require.Equal(t, uint32(200), m.svc.svcByID[id2].sessionAffinityTimeoutSec) 577 578 // LoadBalancer source ranges too 579 require.Equal(t, 2, len(m.svc.svcByID[id2].loadBalancerSourceRanges)) 580 for _, cidr := range []*cidr.CIDR{cidr1, cidr2} { 581 found := false 582 for _, c := range m.svc.svcByID[id2].loadBalancerSourceRanges { 583 if c.String() == cidr.String() { 584 found = true 585 break 586 } 587 } 588 require.Equal(t, true, found) 589 } 590 591 // Maglev lookup table too 592 require.Equal(t, len(backends1), m.lbmap.DummyMaglevTable[uint16(id1)]) 593 require.Equal(t, len(backends2), m.lbmap.DummyMaglevTable[uint16(id2)]) 594 } 595 596 func TestSyncWithK8sFinished(t *testing.T) { 597 m := setupManagerTestSuite(t) 598 599 p1 := &lb.SVC{ 600 Frontend: frontend1, 601 Backends: backends1, 602 Type: lb.SVCTypeNodePort, 603 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 604 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 605 SessionAffinity: true, 606 SessionAffinityTimeoutSec: 300, 607 } 608 _, id1, err := m.svc.UpsertService(p1) 609 require.Nil(t, err) 610 p2 := &lb.SVC{ 611 Frontend: frontend2, 612 Backends: backends2, 613 Type: lb.SVCTypeClusterIP, 614 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 615 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 616 Name: lb.ServiceName{Name: "svc2", Namespace: "ns2"}, 617 } 618 _, _, err = m.svc.UpsertService(p2) 619 require.Nil(t, err) 620 require.Equal(t, 2, len(m.svc.svcByID)) 621 require.Equal(t, 2, len(m.lbmap.AffinityMatch[uint16(id1)])) 622 623 // Restart service, but keep the lbmap to restore services from 624 lbmap := m.svc.lbmap.(*mockmaps.LBMockMap) 625 m.newServiceMock(lbmap) 626 err = m.svc.RestoreServices() 627 require.Nil(t, err) 628 require.Equal(t, 2, len(m.svc.svcByID)) 629 630 // Imitate a situation where svc1 was deleted while we were down. 631 // In real life, the following upsert is called by k8s_watcher during 632 // the sync period of the cilium-agent's k8s service cache which happens 633 // during the initialization of cilium-agent. P2 svc updated affinity is synced. 634 p2.SessionAffinity = true 635 p2.SessionAffinityTimeoutSec = 100 636 _, id2, err := m.svc.UpsertService(p2) 637 require.Nil(t, err) 638 639 // Add non-existing affinity matches 640 lbmap.AddAffinityMatch(20, 300) 641 lbmap.AddAffinityMatch(20, 301) 642 lbmap.AddAffinityMatch(uint16(id1), 302) 643 lbmap.AddAffinityMatch(uint16(id2), 305) 644 645 // cilium-agent finished the initialization, and thus SyncWithK8sFinished 646 // is called 647 stale, err := m.svc.SyncWithK8sFinished(false, nil) 648 require.Nil(t, stale) 649 require.Nil(t, err) 650 651 // svc1 should be removed from cilium while svc2 is synced 652 require.Equal(t, 1, len(m.svc.svcByID)) 653 _, found := m.svc.svcByID[id2] 654 require.Equal(t, true, found) 655 _, found = m.svc.svcByID[id1] 656 require.Equal(t, false, found) 657 require.Equal(t, "svc2", m.svc.svcByID[id2].svcName.Name) 658 require.Equal(t, "ns2", m.svc.svcByID[id2].svcName.Namespace) 659 require.Equal(t, 1, len(m.lbmap.AffinityMatch)) 660 // Check that the non-existing affinity matches were removed 661 matches, _ := lbmap.DumpAffinityMatches() 662 require.Equal(t, 1, len(matches)) // id2 svc has updated session affinity 663 require.Equal(t, 2, len(matches[uint16(id2)])) 664 for _, b := range lbmap.ServiceByID[uint16(id2)].Backends { 665 require.Equal(t, struct{}{}, m.lbmap.AffinityMatch[uint16(id2)][b.ID]) 666 } 667 } 668 669 func TestRestoreServiceWithStaleBackends(t *testing.T) { 670 backendAddrs := []string{"10.0.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.4", "10.0.0.5"} 671 finalBackendAddrs := []string{"10.0.0.2", "10.0.0.3", "10.0.0.5"} 672 673 service := func(ns, name, frontend string, backends ...string) *lb.SVC { 674 var bes []*lb.Backend 675 for _, backend := range backends { 676 bes = append(bes, lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster(backend), 8080)) 677 } 678 679 return &lb.SVC{ 680 Frontend: *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster(frontend), 80, lb.ScopeExternal, 0), 681 Backends: bes, 682 Type: lb.SVCTypeClusterIP, 683 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 684 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 685 Name: lb.ServiceName{Name: name, Namespace: ns}, 686 } 687 } 688 689 toBackendAddrs := func(backends []*lb.Backend) (addrs []string) { 690 for _, be := range backends { 691 addrs = append(addrs, be.L3n4Addr.AddrCluster.Addr().String()) 692 } 693 return 694 } 695 696 tests := []struct { 697 name string 698 localOnly bool 699 isLocal bool 700 expectStaleBackends bool 701 }{ 702 { 703 name: "local only, local service", 704 localOnly: true, 705 isLocal: true, 706 expectStaleBackends: false, 707 }, 708 { 709 name: "local only, global service", 710 localOnly: true, 711 isLocal: false, 712 expectStaleBackends: true, 713 }, 714 { 715 name: "all, local service", 716 localOnly: false, 717 isLocal: true, 718 expectStaleBackends: false, 719 }, 720 { 721 name: "all, global service", 722 localOnly: false, 723 isLocal: false, 724 expectStaleBackends: false, 725 }, 726 } 727 728 for _, tt := range tests { 729 t.Run(tt.name, func(t *testing.T) { 730 lbmap := mockmaps.NewLBMockMap() 731 svc := newService(&FakeMonitorAgent{}, lbmap, nil) 732 733 _, id1, err := svc.upsertService(service("foo", "bar", "172.16.0.1", backendAddrs...)) 734 require.NoError(t, err, "Failed to upsert service") 735 736 require.Contains(t, lbmap.ServiceByID, uint16(id1), "lbmap not populated correctly") 737 require.ElementsMatch(t, backendAddrs, toBackendAddrs(lbmap.ServiceByID[uint16(id1)].Backends), "lbmap not populated correctly") 738 require.ElementsMatch(t, backendAddrs, toBackendAddrs(maps.Values(lbmap.BackendByID)), "lbmap not populated correctly") 739 740 // Recreate the Service structure, but keep the lbmap to restore services from 741 svc = newService(&FakeMonitorAgent{}, lbmap, nil) 742 require.NoError(t, svc.RestoreServices(), "Failed to restore services") 743 744 // Simulate a set of service updates. Until synchronization completes, a given service 745 // might not yet contain all backends, in case they either belong to different endpointslices 746 // or different clusters. 747 _, id1bis, err := svc.upsertService(service("foo", "bar", "172.16.0.1", "10.0.0.3")) 748 require.NoError(t, err, "Failed to upsert service") 749 require.Equal(t, id1, id1bis, "Service ID changed unexpectedly") 750 751 // No backend should have been removed yet 752 require.Contains(t, lbmap.ServiceByID, uint16(id1), "lbmap incorrectly modified") 753 require.ElementsMatch(t, backendAddrs, toBackendAddrs(lbmap.ServiceByID[uint16(id1)].Backends), "lbmap incorrectly modified") 754 require.ElementsMatch(t, backendAddrs, toBackendAddrs(maps.Values(lbmap.BackendByID)), "lbmap incorrectly modified") 755 756 // Let's do it once more 757 _, id1ter, err := svc.upsertService(service("foo", "bar", "172.16.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.5")) 758 require.NoError(t, err, "Failed to upsert service") 759 require.Equal(t, id1, id1ter, "Service ID changed unexpectedly") 760 761 // No backend should have been removed yet 762 require.Contains(t, lbmap.ServiceByID, uint16(id1), "lbmap incorrectly modified") 763 require.ElementsMatch(t, backendAddrs, toBackendAddrs(lbmap.ServiceByID[uint16(id1)].Backends), "lbmap incorrectly modified") 764 require.ElementsMatch(t, backendAddrs, toBackendAddrs(maps.Values(lbmap.BackendByID)), "lbmap incorrectly modified") 765 766 svcID := k8s.ServiceID{Namespace: "foo", Name: "bar"} 767 localServices := sets.New[k8s.ServiceID]() 768 if tt.isLocal { 769 localServices.Insert(svcID) 770 } 771 772 stale, err := svc.SyncWithK8sFinished(tt.localOnly, localServices) 773 require.NoError(t, err, "Failed to trigger garbage collection") 774 775 require.Contains(t, lbmap.ServiceByID, uint16(id1), "service incorrectly removed from lbmap") 776 777 // Stale backends should now have been removed (if appropriate) 778 if tt.expectStaleBackends { 779 require.Empty(t, stale) 780 require.ElementsMatch(t, backendAddrs, toBackendAddrs(lbmap.ServiceByID[uint16(id1)].Backends), "stale backends should not have been removed from lbmap") 781 require.ElementsMatch(t, backendAddrs, toBackendAddrs(maps.Values(lbmap.BackendByID)), "stale backends should not have been removed from lbmap") 782 } else { 783 require.ElementsMatch(t, stale, []k8s.ServiceID{svcID}) 784 785 // Trigger a new upsertion: this mimics what would eventually happen when calling ServiceCache.EnsureService() 786 _, _, err := svc.upsertService(service("foo", "bar", "172.16.0.1", "10.0.0.2", "10.0.0.3", "10.0.0.5")) 787 require.NoError(t, err, "Failed to upsert service") 788 789 require.ElementsMatch(t, finalBackendAddrs, toBackendAddrs(lbmap.ServiceByID[uint16(id1)].Backends), "stale backends not correctly removed from lbmap") 790 require.ElementsMatch(t, finalBackendAddrs, toBackendAddrs(maps.Values(lbmap.BackendByID)), "stale backends not correctly removed from lbmap") 791 } 792 }) 793 } 794 } 795 796 func TestHealthCheckNodePort(t *testing.T) { 797 m := setupManagerTestSuite(t) 798 799 // Create two frontends, one for LoadBalaner and one for ClusterIP. 800 // This is used to emulate how we get K8s services from the K8s watcher, 801 // i.e. one service per frontend (even if it is logically the same service) 802 loadBalancerIP := *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.1"), 80, lb.ScopeExternal, 0) 803 clusterIP := *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("10.20.30.40"), 80, lb.ScopeExternal, 0) 804 805 // Create two node-local backends 806 localBackend1 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.1"), 8080) 807 localBackend2 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.2"), 8080) 808 localTerminatingBackend3 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.3"), 8080) 809 localBackend1.NodeName = nodeTypes.GetName() 810 localBackend2.NodeName = nodeTypes.GetName() 811 localTerminatingBackend3.NodeName = nodeTypes.GetName() 812 localActiveBackends := []*lb.Backend{localBackend1, localBackend2} 813 814 // Create three remote backends 815 remoteBackend1 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.3"), 8080) 816 remoteBackend2 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.4"), 8080) 817 remoteBackend3 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.5"), 8080) 818 remoteBackend1.NodeName = "not-" + nodeTypes.GetName() 819 remoteBackend2.NodeName = "not-" + nodeTypes.GetName() 820 remoteBackend3.NodeName = "not-" + nodeTypes.GetName() 821 remoteBackends := []*lb.Backend{remoteBackend1, remoteBackend2, remoteBackend3} 822 823 allBackends := []*lb.Backend{localBackend1, localBackend2, localTerminatingBackend3, remoteBackend1, remoteBackend2, remoteBackend3} 824 825 // Insert svc1 as type LoadBalancer with some local backends 826 p1 := &lb.SVC{ 827 Frontend: loadBalancerIP, 828 Backends: allBackends, 829 Type: lb.SVCTypeLoadBalancer, 830 ExtTrafficPolicy: lb.SVCTrafficPolicyLocal, 831 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 832 HealthCheckNodePort: 32001, 833 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 834 } 835 _, id1, err := m.svc.UpsertService(p1) 836 require.Nil(t, err) 837 require.Equal(t, "svc1", m.svcHealth.ServiceByPort(32001).Service.Name) 838 require.Equal(t, "ns1", m.svcHealth.ServiceByPort(32001).Service.Namespace) 839 840 p1.Backends[2].State = lb.BackendStateTerminating 841 _, _, _ = m.svc.UpsertService(p1) 842 require.Equal(t, len(localActiveBackends), m.svcHealth.ServiceByPort(32001).LocalEndpoints) 843 844 // Insert the ClusterIP frontend of svc1 845 p2 := &lb.SVC{ 846 Frontend: clusterIP, 847 Backends: allBackends, 848 Type: lb.SVCTypeClusterIP, 849 ExtTrafficPolicy: lb.SVCTrafficPolicyLocal, 850 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 851 HealthCheckNodePort: 32001, 852 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 853 } 854 _, id2, err := m.svc.UpsertService(p2) 855 require.Nil(t, err) 856 require.Equal(t, "svc1", m.svcHealth.ServiceByPort(32001).Service.Name) 857 require.Equal(t, "ns1", m.svcHealth.ServiceByPort(32001).Service.Namespace) 858 require.Equal(t, len(localActiveBackends), m.svcHealth.ServiceByPort(32001).LocalEndpoints) 859 860 // Update the HealthCheckNodePort for svc1 861 p1.HealthCheckNodePort = 32000 862 new, _, err := m.svc.UpsertService(p1) 863 require.Nil(t, err) 864 require.Equal(t, false, new) 865 require.Equal(t, "svc1", m.svcHealth.ServiceByPort(32000).Service.Name) 866 require.Equal(t, "ns1", m.svcHealth.ServiceByPort(32000).Service.Namespace) 867 require.Equal(t, len(localActiveBackends), m.svcHealth.ServiceByPort(32000).LocalEndpoints) 868 require.Nil(t, m.svcHealth.ServiceByPort(32001)) 869 870 // Update the externalTrafficPolicy for svc1 871 p1.ExtTrafficPolicy = lb.SVCTrafficPolicyCluster 872 p1.HealthCheckNodePort = 0 873 new, _, err = m.svc.UpsertService(p1) 874 require.Nil(t, err) 875 require.Equal(t, false, new) 876 require.Nil(t, m.svcHealth.ServiceByPort(32000)) 877 require.Nil(t, m.svcHealth.ServiceByPort(32001)) 878 879 // Restore the original version of svc1 880 p1.ExtTrafficPolicy = lb.SVCTrafficPolicyLocal 881 p1.HealthCheckNodePort = 32001 882 new, _, err = m.svc.UpsertService(p1) 883 require.Nil(t, err) 884 require.Equal(t, false, new) 885 require.Equal(t, "svc1", m.svcHealth.ServiceByPort(32001).Service.Name) 886 require.Equal(t, "ns1", m.svcHealth.ServiceByPort(32001).Service.Namespace) 887 require.Equal(t, len(localActiveBackends), m.svcHealth.ServiceByPort(32001).LocalEndpoints) 888 889 // Upsert svc1 of type LoadBalancer with only remote backends 890 p1.Backends = remoteBackends 891 new, _, err = m.svc.UpsertService(p1) 892 require.Nil(t, err) 893 require.Equal(t, false, new) 894 require.Equal(t, "svc1", m.svcHealth.ServiceByPort(32001).Service.Name) 895 require.Equal(t, "ns1", m.svcHealth.ServiceByPort(32001).Service.Namespace) 896 require.Equal(t, 0, m.svcHealth.ServiceByPort(32001).LocalEndpoints) 897 898 // Upsert svc1 of type ClusterIP with only remote backends 899 p2.Backends = remoteBackends 900 new, _, err = m.svc.UpsertService(p2) 901 require.Nil(t, err) 902 require.Equal(t, false, new) 903 require.Equal(t, "svc1", m.svcHealth.ServiceByPort(32001).Service.Name) 904 require.Equal(t, "ns1", m.svcHealth.ServiceByPort(32001).Service.Namespace) 905 require.Equal(t, 0, m.svcHealth.ServiceByPort(32001).LocalEndpoints) 906 907 // Delete svc1 of type LoadBalancer 908 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 909 require.Nil(t, err) 910 require.Equal(t, true, found) 911 require.Nil(t, m.svcHealth.ServiceByPort(32001)) 912 913 // Delete svc1 of type ClusterIP 914 found, err = m.svc.DeleteServiceByID(lb.ServiceID(id2)) 915 require.Nil(t, err) 916 require.Equal(t, true, found) 917 require.Nil(t, m.svcHealth.ServiceByPort(32001)) 918 } 919 920 // Define a mock implementation of the NodeMetaCollector interface for testing 921 type mockNodeMetaCollector struct { 922 ipv4 net.IP 923 ipv6 net.IP 924 } 925 926 func (m *mockNodeMetaCollector) GetIPv4() net.IP { 927 return m.ipv4 928 } 929 930 func (m *mockNodeMetaCollector) GetIPv6() net.IP { 931 return m.ipv6 932 } 933 934 func TestHealthCheckLoadBalancerIP(t *testing.T) { 935 m := setupManagerTestSuite(t) 936 937 option.Config.EnableHealthCheckLoadBalancerIP = true 938 939 mockCollector := &mockNodeMetaCollector{ 940 ipv4: net.ParseIP("192.0.2.0"), 941 ipv6: net.ParseIP("2001:db8::1"), 942 } 943 944 loadBalancerIP := *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.1"), 80, lb.ScopeExternal, 0) 945 946 localBackend1 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.1"), 8080) 947 localBackend1.NodeName = nodeTypes.GetName() 948 949 allBackends := []*lb.Backend{localBackend1} 950 951 // Insert svc1 as type LoadBalancer with some local backends 952 p1 := &lb.SVC{ 953 Frontend: loadBalancerIP, 954 Backends: allBackends, 955 Type: lb.SVCTypeLoadBalancer, 956 ExtTrafficPolicy: lb.SVCTrafficPolicyLocal, 957 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 958 HealthCheckNodePort: 32001, 959 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 960 } 961 962 svc, _, _, _, _ := m.svc.createSVCInfoIfNotExist(p1) 963 err := m.svc.upsertNodePortHealthService(svc, mockCollector) 964 965 require.Nil(t, err) 966 require.NotEqual(t, "", svc.healthcheckFrontendHash) 967 require.Equal(t, "svc1-healthCheck", m.svc.svcByHash[svc.healthcheckFrontendHash].svcName.Name) 968 require.Equal(t, "ns1", m.svc.svcByHash[svc.healthcheckFrontendHash].svcName.Namespace) 969 require.Equal(t, svc.svcHealthCheckNodePort, m.svc.svcByHash[svc.healthcheckFrontendHash].frontend.Port) 970 require.Equal(t, netip.MustParseAddr("1.1.1.1"), m.svc.svcByHash[svc.healthcheckFrontendHash].frontend.AddrCluster.Addr()) 971 require.Equal(t, cmtypes.AddrClusterFrom(netip.MustParseAddr("192.0.2.0"), option.Config.ClusterID), m.svc.svcByHash[svc.healthcheckFrontendHash].backends[0].AddrCluster) 972 973 // Update the externalTrafficPolicy for svc1 974 svc.frontend.Scope = lb.ScopeExternal 975 svc.svcHealthCheckNodePort = 0 976 oldHealthHash := svc.healthcheckFrontendHash 977 err = m.svc.upsertNodePortHealthService(svc, mockCollector) 978 require.Nil(t, err) 979 require.Equal(t, "", svc.healthcheckFrontendHash) 980 require.Nil(t, m.svc.svcByHash[oldHealthHash]) 981 982 // Restore the original version of svc1 983 svc.frontend.Scope = lb.ScopeInternal 984 svc.svcHealthCheckNodePort = 32001 985 err = m.svc.upsertNodePortHealthService(svc, mockCollector) 986 require.Nil(t, err) 987 require.NotEqual(t, "", svc.healthcheckFrontendHash) 988 require.Equal(t, "svc1-healthCheck", m.svc.svcByHash[svc.healthcheckFrontendHash].svcName.Name) 989 require.Equal(t, "ns1", m.svc.svcByHash[svc.healthcheckFrontendHash].svcName.Namespace) 990 require.Equal(t, svc.svcHealthCheckNodePort, m.svc.svcByHash[svc.healthcheckFrontendHash].frontend.Port) 991 require.Equal(t, netip.MustParseAddr("1.1.1.1"), m.svc.svcByHash[svc.healthcheckFrontendHash].frontend.AddrCluster.Addr()) 992 require.Equal(t, cmtypes.AddrClusterFrom(netip.MustParseAddr("192.0.2.0"), option.Config.ClusterID), m.svc.svcByHash[svc.healthcheckFrontendHash].backends[0].AddrCluster) 993 994 // IPv6 NodePort Backend 995 oldHealthHash = svc.healthcheckFrontendHash 996 svc.frontend = *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("2001:db8:1::1"), 80, lb.ScopeExternal, 0) 997 err = m.svc.upsertNodePortHealthService(svc, mockCollector) 998 require.Nil(t, err) 999 require.Equal(t, cmtypes.AddrClusterFrom(netip.MustParseAddr("2001:db8::1"), option.Config.ClusterID), m.svc.svcByHash[svc.healthcheckFrontendHash].backends[0].AddrCluster) 1000 require.Nil(t, m.svc.svcByHash[oldHealthHash]) 1001 1002 var ok bool 1003 // Delete 1004 ok, err = m.svc.DeleteService(m.svc.svcByHash[svc.healthcheckFrontendHash].frontend.L3n4Addr) 1005 require.Equal(t, true, ok) 1006 require.Nil(t, err) 1007 1008 option.Config.EnableHealthCheckLoadBalancerIP = false 1009 } 1010 1011 func TestHealthCheckNodePortDisabled(t *testing.T) { 1012 m := setupManagerTestSuite(t) 1013 1014 // NewService sets healthServer to nil if EnableHealthCheckNodePort is 1015 // false at start time. We emulate this here by temporarily setting it nil. 1016 enableHealthCheckNodePort := option.Config.EnableHealthCheckNodePort 1017 healthServer := m.svc.healthServer 1018 option.Config.EnableHealthCheckNodePort = false 1019 m.svc.healthServer = nil 1020 defer func() { 1021 option.Config.EnableHealthCheckNodePort = enableHealthCheckNodePort 1022 m.svc.healthServer = healthServer 1023 }() 1024 1025 p1 := &lb.SVC{ 1026 Frontend: frontend1, 1027 Backends: backends1, 1028 Type: lb.SVCTypeNodePort, 1029 ExtTrafficPolicy: lb.SVCTrafficPolicyLocal, 1030 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1031 HealthCheckNodePort: 32000, 1032 } 1033 _, id1, err := m.svc.UpsertService(p1) 1034 require.Nil(t, err) 1035 1036 // Unset HealthCheckNodePort for that service 1037 p1.HealthCheckNodePort = 0 1038 p1.ExtTrafficPolicy = lb.SVCTrafficPolicyCluster 1039 _, _, err = m.svc.UpsertService(p1) 1040 require.Nil(t, err) 1041 1042 // Set HealthCheckNodePort for that service 1043 p1.HealthCheckNodePort = 32000 1044 p1.ExtTrafficPolicy = lb.SVCTrafficPolicyLocal 1045 _, _, err = m.svc.UpsertService(p1) 1046 require.Nil(t, err) 1047 1048 // Delete service with active HealthCheckNodePort 1049 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 1050 require.Nil(t, err) 1051 require.Equal(t, true, found) 1052 } 1053 1054 func TestGetServiceNameByAddr(t *testing.T) { 1055 m := setupManagerTestSuite(t) 1056 1057 fe := frontend1.DeepCopy() 1058 name := "svc1" 1059 namespace := "ns1" 1060 hcport := uint16(3) 1061 p := &lb.SVC{ 1062 Frontend: *fe, 1063 Backends: backends1, 1064 Type: lb.SVCTypeNodePort, 1065 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1066 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1067 HealthCheckNodePort: hcport, 1068 Name: lb.ServiceName{Name: name, Namespace: namespace}, 1069 } 1070 created, id1, err := m.svc.UpsertService(p) 1071 require.Nil(t, err) 1072 require.Equal(t, true, created) 1073 require.Equal(t, lb.ID(1), id1) 1074 fe.ID = id1 1075 gotNamespace, gotName, ok := m.svc.GetServiceNameByAddr(frontend1.L3n4Addr) 1076 require.Equal(t, namespace, gotNamespace) 1077 require.Equal(t, name, gotName) 1078 require.Equal(t, true, ok) 1079 _, _, ok = m.svc.GetServiceNameByAddr(frontend2.L3n4Addr) 1080 require.Equal(t, false, ok) 1081 } 1082 1083 func TestLocalRedirectLocalBackendSelection(t *testing.T) { 1084 m := setupManagerTestSuite(t) 1085 1086 // Create a node-local backend. 1087 localBackend := backends1[0] 1088 localBackend.NodeName = nodeTypes.GetName() 1089 localBackends := []*lb.Backend{localBackend} 1090 // Create two remote backends. 1091 remoteBackends := make([]*lb.Backend, 0, len(backends2)) 1092 for _, backend := range backends2 { 1093 backend.NodeName = "not-" + nodeTypes.GetName() 1094 remoteBackends = append(remoteBackends, backend) 1095 } 1096 allBackends := make([]*lb.Backend, 0, 1+len(remoteBackends)) 1097 allBackends = append(allBackends, localBackend) 1098 allBackends = append(allBackends, remoteBackends...) 1099 1100 // Create a service entry of type Local Redirect. 1101 p1 := &lb.SVC{ 1102 Frontend: frontend1, 1103 Backends: allBackends, 1104 Type: lb.SVCTypeLocalRedirect, 1105 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1106 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1107 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1108 } 1109 // Insert the service entry of type Local Redirect. 1110 created, id, err := m.svc.UpsertService(p1) 1111 require.Nil(t, err) 1112 require.Equal(t, true, created) 1113 require.NotEqual(t, lb.ID(0), id) 1114 1115 svc, ok := m.svc.svcByID[id] 1116 require.Equal(t, true, ok) 1117 require.Equal(t, "ns1", svc.svcName.Namespace) 1118 require.Equal(t, "svc1", svc.svcName.Name) 1119 // Only node-local backends are selected 1120 require.Equal(t, len(localBackends), len(svc.backends)) 1121 1122 svcFromLbMap, ok := m.lbmap.ServiceByID[uint16(id)] 1123 require.Equal(t, true, ok) 1124 require.Equal(t, len(svc.backends), len(svcFromLbMap.Backends)) 1125 } 1126 1127 // Local redirect service should be able to override a ClusterIP service with same 1128 // frontend, but reverse should produce an error. Also, it should not override 1129 // any other type besides itself or clusterIP type. 1130 func TestLocalRedirectServiceOverride(t *testing.T) { 1131 m := setupManagerTestSuite(t) 1132 1133 // Create a node-local backend. 1134 localBackend := backends1[0] 1135 localBackend.NodeName = nodeTypes.GetName() 1136 localBackends := []*lb.Backend{localBackend} 1137 // Create two remote backends. 1138 remoteBackends := make([]*lb.Backend, 0, len(backends2)) 1139 for _, backend := range backends2 { 1140 backend.NodeName = "not-" + nodeTypes.GetName() 1141 remoteBackends = append(remoteBackends, backend) 1142 } 1143 allBackends := make([]*lb.Backend, 0, 1+len(remoteBackends)) 1144 allBackends = append(allBackends, localBackend) 1145 allBackends = append(allBackends, remoteBackends...) 1146 1147 p1 := &lb.SVC{ 1148 Frontend: frontend1, 1149 Backends: allBackends, 1150 Type: lb.SVCTypeClusterIP, 1151 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1152 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1153 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1154 } 1155 1156 // Insert the service entry of type ClusterIP. 1157 created, id, err := m.svc.UpsertService(p1) 1158 require.Nil(t, err) 1159 require.Equal(t, true, created) 1160 require.NotEqual(t, lb.ID(0), id) 1161 1162 svc, ok := m.svc.svcByID[id] 1163 require.Equal(t, len(allBackends), len(svc.backends)) 1164 require.Equal(t, true, ok) 1165 1166 // Insert the service entry of type Local Redirect. 1167 p1.Type = lb.SVCTypeLocalRedirect 1168 created, id, err = m.svc.UpsertService(p1) 1169 1170 // Local redirect service should override the ClusterIP service with node-local backends. 1171 require.Nil(t, err) 1172 require.Equal(t, false, created) 1173 require.NotEqual(t, lb.ID(0), id) 1174 svc = m.svc.svcByID[id] 1175 // Only node-local backends are selected. 1176 require.Equal(t, len(localBackends), len(svc.backends)) 1177 1178 // Insert the service entry of type ClusterIP. 1179 p1.Type = lb.SVCTypeClusterIP 1180 created, _, err = m.svc.UpsertService(p1) 1181 1182 require.Error(t, err) 1183 require.Equal(t, false, created) 1184 1185 p2 := &lb.SVC{ 1186 Frontend: frontend2, 1187 Backends: allBackends, 1188 Type: lb.SVCTypeNodePort, 1189 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1190 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1191 Name: lb.ServiceName{Name: "svc2", Namespace: "ns1"}, 1192 } 1193 1194 // Insert the service entry of type NodePort. 1195 created, id, err = m.svc.UpsertService(p2) 1196 require.Nil(t, err) 1197 require.Equal(t, true, created) 1198 require.NotEqual(t, lb.ID(0), id) 1199 1200 svc, ok = m.svc.svcByID[id] 1201 require.Equal(t, len(allBackends), len(svc.backends)) 1202 require.Equal(t, true, ok) 1203 1204 // Insert the service entry of type Local Redirect. 1205 p2.Type = lb.SVCTypeLocalRedirect 1206 created, _, err = m.svc.UpsertService(p2) 1207 1208 // Local redirect service should not override the NodePort service. 1209 require.Error(t, err) 1210 require.Equal(t, false, created) 1211 } 1212 1213 // Tests whether upsert service handles terminating backends, whereby terminating 1214 // backends are not added to the service map, but are added to the backends and 1215 // affinity maps. 1216 func TestUpsertServiceWithTerminatingBackends(t *testing.T) { 1217 m := setupManagerTestSuite(t) 1218 1219 option.Config.NodePortAlg = option.NodePortAlgMaglev 1220 backends := append(backends4, backends1...) 1221 p := &lb.SVC{ 1222 Frontend: frontend1, 1223 Backends: backends, 1224 Type: lb.SVCTypeNodePort, 1225 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1226 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1227 SessionAffinity: true, 1228 SessionAffinityTimeoutSec: 100, 1229 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1230 } 1231 1232 created, id1, err := m.svc.UpsertService(p) 1233 1234 require.Nil(t, err) 1235 require.Equal(t, true, created) 1236 require.Equal(t, lb.ID(1), id1) 1237 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1238 require.Equal(t, len(backends), m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1239 1240 p.Backends[0].State = lb.BackendStateTerminating 1241 1242 _, _, err = m.svc.UpsertService(p) 1243 1244 require.Nil(t, err) 1245 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1246 require.Equal(t, len(backends1), m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1247 // Sorted active backends by ID first followed by non-active 1248 require.Equal(t, lb.BackendID(2), m.lbmap.ServiceByID[uint16(id1)].Backends[0].ID) 1249 require.Equal(t, lb.BackendID(3), m.lbmap.ServiceByID[uint16(id1)].Backends[1].ID) 1250 require.Equal(t, lb.BackendID(1), m.lbmap.ServiceByID[uint16(id1)].Backends[2].ID) 1251 require.Equal(t, 3, len(m.lbmap.BackendByID)) 1252 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 1253 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 1254 require.Equal(t, 3, len(m.lbmap.AffinityMatch[uint16(id1)])) 1255 for bID := range m.lbmap.BackendByID { 1256 require.Equal(t, struct{}{}, m.lbmap.AffinityMatch[uint16(id1)][bID]) 1257 } 1258 require.Equal(t, len(backends1), m.lbmap.DummyMaglevTable[uint16(id1)]) 1259 1260 // Delete terminating backends. 1261 p.Backends = []*lb.Backend{} 1262 1263 created, id1, err = m.svc.UpsertService(p) 1264 1265 require.Nil(t, err) 1266 require.Equal(t, false, created) 1267 require.Equal(t, lb.ID(1), id1) 1268 require.Equal(t, 0, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1269 require.Equal(t, 0, len(m.lbmap.BackendByID)) 1270 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 1271 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 1272 require.Equal(t, 0, len(m.lbmap.AffinityMatch[uint16(id1)])) 1273 } 1274 1275 // TestUpsertServiceWithOnlyTerminatingBackends tests that a terminating backend is still 1276 // used if there are not active backends. 1277 func TestUpsertServiceWithOnlyTerminatingBackends(t *testing.T) { 1278 m := setupManagerTestSuite(t) 1279 1280 option.Config.NodePortAlg = option.NodePortAlgMaglev 1281 backends := backends1 // There are 2 backends 1282 p := &lb.SVC{ 1283 Frontend: frontend1, 1284 Backends: backends, 1285 Type: lb.SVCTypeNodePort, 1286 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1287 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1288 SessionAffinity: true, 1289 SessionAffinityTimeoutSec: 100, 1290 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1291 } 1292 1293 created, id1, err := m.svc.UpsertService(p) 1294 1295 require.Nil(t, err) 1296 require.Equal(t, true, created) 1297 require.Equal(t, lb.ID(1), id1) 1298 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1299 require.Equal(t, 2, m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1300 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 1301 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 1302 1303 // The terminating backend should not be considered 1304 p.Backends[1].State = lb.BackendStateTerminating 1305 1306 created, id1, err = m.svc.UpsertService(p) 1307 1308 require.Nil(t, err) 1309 require.Equal(t, false, created) 1310 require.Equal(t, lb.ID(1), id1) 1311 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1312 require.Equal(t, 2, len(m.lbmap.BackendByID)) 1313 require.Equal(t, 1, m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1314 1315 // Delete terminating backends. 1316 p.Backends = p.Backends[:1] 1317 1318 created, id1, err = m.svc.UpsertService(p) 1319 1320 require.Nil(t, err) 1321 require.Equal(t, false, created) 1322 require.Equal(t, lb.ID(1), id1) 1323 require.Equal(t, 1, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1324 require.Equal(t, 1, len(m.lbmap.BackendByID)) 1325 require.Equal(t, 1, len(m.lbmap.AffinityMatch[uint16(id1)])) 1326 1327 // The terminating backend should be considered since there are no more active 1328 p.Backends[0].State = lb.BackendStateTerminating 1329 1330 created, id1, err = m.svc.UpsertService(p) 1331 1332 require.Nil(t, err) 1333 require.Equal(t, false, created) 1334 require.Equal(t, lb.ID(1), id1) 1335 require.Equal(t, 1, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1336 require.Equal(t, 1, len(m.lbmap.BackendByID)) 1337 require.Equal(t, 0, m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1338 1339 // Delete terminating backends. 1340 p.Backends = []*lb.Backend{} 1341 1342 created, id1, err = m.svc.UpsertService(p) 1343 1344 require.Nil(t, err) 1345 require.Equal(t, false, created) 1346 require.Equal(t, lb.ID(1), id1) 1347 require.Equal(t, 0, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1348 require.Equal(t, 0, len(m.lbmap.BackendByID)) 1349 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 1350 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 1351 require.Equal(t, 0, len(m.lbmap.AffinityMatch[uint16(id1)])) 1352 } 1353 1354 // Tests whether upsert service provisions the Maglev LUT for ClusterIP, 1355 // if ExternalClusterIP is true 1356 func TestUpsertServiceWithExternalClusterIP(t *testing.T) { 1357 m := setupManagerTestSuite(t) 1358 1359 option.Config.NodePortAlg = option.NodePortAlgMaglev 1360 option.Config.ExternalClusterIP = true 1361 backends := make([]*lb.Backend, 0, len(backends1)) 1362 for _, b := range backends1 { 1363 backends = append(backends, b.DeepCopy()) 1364 } 1365 backends[0].State = lb.BackendStateActive 1366 backends[1].State = lb.BackendStateActive 1367 p := &lb.SVC{ 1368 Frontend: frontend1, 1369 Backends: backends, 1370 Type: lb.SVCTypeClusterIP, 1371 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1372 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1373 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1374 } 1375 1376 created, id1, err := m.svc.UpsertService(p) 1377 1378 require.Nil(t, err) 1379 require.Equal(t, true, created) 1380 require.Equal(t, lb.ID(1), id1) 1381 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1382 require.Equal(t, 2, len(m.lbmap.BackendByID)) 1383 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 1384 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 1385 require.Equal(t, len(backends), m.lbmap.DummyMaglevTable[uint16(id1)]) 1386 } 1387 1388 // Tests whether upsert service doesn't provision the Maglev LUT for ClusterIP, 1389 // if ExternalClusterIP is false 1390 func TestUpsertServiceWithOutExternalClusterIP(t *testing.T) { 1391 m := setupManagerTestSuite(t) 1392 1393 option.Config.NodePortAlg = option.NodePortAlgMaglev 1394 p := &lb.SVC{ 1395 Frontend: frontend1, 1396 Backends: backends1, 1397 Type: lb.SVCTypeClusterIP, 1398 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1399 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1400 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1401 } 1402 1403 created, id1, err := m.svc.UpsertService(p) 1404 1405 require.Nil(t, err) 1406 require.Equal(t, true, created) 1407 require.Equal(t, lb.ID(1), id1) 1408 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1409 require.Equal(t, 2, len(m.lbmap.BackendByID)) 1410 require.Equal(t, "svc1", m.svc.svcByID[id1].svcName.Name) 1411 require.Equal(t, "ns1", m.svc.svcByID[id1].svcName.Namespace) 1412 require.Equal(t, 0, m.lbmap.DummyMaglevTable[uint16(id1)]) 1413 } 1414 1415 // Tests terminating backend entries are not removed after service restore. 1416 func TestRestoreServiceWithTerminatingBackends(t *testing.T) { 1417 m := setupManagerTestSuite(t) 1418 1419 option.Config.NodePortAlg = option.NodePortAlgMaglev 1420 backends := append(backends4, backends1...) 1421 p := &lb.SVC{ 1422 Frontend: frontend1, 1423 Backends: backends, 1424 Type: lb.SVCTypeNodePort, 1425 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1426 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1427 SessionAffinity: true, 1428 SessionAffinityTimeoutSec: 100, 1429 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1430 } 1431 1432 created, id1, err := m.svc.UpsertService(p) 1433 1434 t.Log(m.lbmap.ServiceByID[0]) 1435 require.Nil(t, err) 1436 require.Equal(t, true, created) 1437 require.Equal(t, lb.ID(1), id1) 1438 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1439 require.Equal(t, len(backends), m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1440 1441 p.Backends[0].State = lb.BackendStateTerminating 1442 1443 _, _, err = m.svc.UpsertService(p) 1444 1445 require.Nil(t, err) 1446 1447 // Simulate agent restart. 1448 lbmap := m.svc.lbmap.(*mockmaps.LBMockMap) 1449 m.newServiceMock(lbmap) 1450 1451 // Restore services from lbmap 1452 err = m.svc.RestoreServices() 1453 require.Nil(t, err) 1454 1455 // Backends including terminating ones have been restored 1456 require.Equal(t, 3, len(m.svc.backendByHash)) 1457 for _, b := range backends1 { 1458 _, found := m.svc.backendByHash[b.Hash()] 1459 require.Equal(t, true, found) 1460 } 1461 1462 // Affinity matches including terminating ones were restored 1463 matches, _ := m.lbmap.DumpAffinityMatches() 1464 require.Equal(t, 1, len(matches)) 1465 require.Equal(t, 3, len(matches[uint16(id1)])) 1466 for _, b := range m.lbmap.ServiceByID[uint16(id1)].Backends { 1467 require.Equal(t, struct{}{}, m.lbmap.AffinityMatch[uint16(id1)][b.ID]) 1468 } 1469 require.Equal(t, len(backends1), m.lbmap.DummyMaglevTable[uint16(id1)]) 1470 } 1471 1472 // l7 load balancer service should be able to override any service type 1473 // (Cluster IP, NodePort, etc.) with same frontend. 1474 func TestL7LoadBalancerServiceOverride(t *testing.T) { 1475 m := setupManagerTestSuite(t) 1476 1477 // Create a node-local backend. 1478 localBackend := backends1[0] 1479 localBackend.NodeName = nodeTypes.GetName() 1480 // Create two remote backends. 1481 remoteBackends := make([]*lb.Backend, 0, len(backends2)) 1482 for _, backend := range backends2 { 1483 backend.NodeName = "not-" + nodeTypes.GetName() 1484 remoteBackends = append(remoteBackends, backend) 1485 } 1486 allBackends := make([]*lb.Backend, 0, 1+len(remoteBackends)) 1487 allBackends = append(allBackends, localBackend) 1488 allBackends = append(allBackends, remoteBackends...) 1489 1490 p1 := &lb.SVC{ 1491 Frontend: frontend1, 1492 Backends: allBackends, 1493 Type: lb.SVCTypeClusterIP, 1494 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1495 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1496 Name: lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"}, 1497 } 1498 1499 // Insert the service entry of type ClusterIP. 1500 created, id, err := m.svc.UpsertService(p1) 1501 require.Nil(t, err) 1502 require.Equal(t, true, created) 1503 require.NotEqual(t, lb.ID(0), id) 1504 1505 svc, ok := m.svc.svcByID[id] 1506 require.Equal(t, len(allBackends), len(svc.backends)) 1507 require.Equal(t, true, ok) 1508 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1509 1510 // registering redirection with proxy port 0 should result in an error 1511 echoOtherNode := lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"} 1512 resource1 := L7LBResourceName{Name: "testOwner1", Namespace: "cilium-test"} 1513 err = m.svc.RegisterL7LBServiceRedirect(echoOtherNode, resource1, 0, nil) 1514 require.Error(t, err) 1515 1516 svc, ok = m.svc.svcByID[id] 1517 require.Equal(t, len(allBackends), len(svc.backends)) 1518 require.Equal(t, true, ok) 1519 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1520 1521 // Registering with redirection stores the proxy port. 1522 resource2 := L7LBResourceName{Name: "testOwner2", Namespace: "cilium-test"} 1523 err = m.svc.RegisterL7LBServiceRedirect(echoOtherNode, resource2, 9090, nil) 1524 require.Nil(t, err) 1525 1526 svc, ok = m.svc.svcByID[id] 1527 require.Equal(t, len(allBackends), len(svc.backends)) 1528 require.Equal(t, true, ok) 1529 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1530 1531 // registering redirection for a Service that already has a redirect registration 1532 // should result in an error. 1533 resource3 := L7LBResourceName{Name: "testOwner3", Namespace: "cilium-test"} 1534 err = m.svc.RegisterL7LBServiceRedirect(echoOtherNode, resource3, 10000, nil) 1535 require.Error(t, err) 1536 1537 // Remove with an unregistered owner name does not remove 1538 resource4 := L7LBResourceName{Name: "testOwner4", Namespace: "cilium-test"} 1539 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource4) 1540 require.Nil(t, err) 1541 1542 svc, ok = m.svc.svcByID[id] 1543 require.Equal(t, len(allBackends), len(svc.backends)) 1544 require.Equal(t, true, ok) 1545 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1546 1547 // Removing registration without redirection does not remove the proxy port 1548 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource1) 1549 require.Nil(t, err) 1550 1551 svc, ok = m.svc.svcByID[id] 1552 require.Equal(t, len(allBackends), len(svc.backends)) 1553 require.Equal(t, true, ok) 1554 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1555 1556 // removing the registration with redirection removes the proxy port 1557 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource2) 1558 require.Nil(t, err) 1559 1560 svc, ok = m.svc.svcByID[id] 1561 require.Equal(t, len(allBackends), len(svc.backends)) 1562 require.Equal(t, true, ok) 1563 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1564 } 1565 1566 // l7 load balancer service with ports should only override the given frontend ports. 1567 func TestL7LoadBalancerServiceOverrideWithPorts(t *testing.T) { 1568 m := setupManagerTestSuite(t) 1569 1570 // Create a node-local backend. 1571 localBackend := backends1[0] 1572 localBackend.NodeName = nodeTypes.GetName() 1573 // Create two remote backends. 1574 remoteBackends := make([]*lb.Backend, 0, len(backends2)) 1575 for _, backend := range backends2 { 1576 backend.NodeName = "not-" + nodeTypes.GetName() 1577 remoteBackends = append(remoteBackends, backend) 1578 } 1579 allBackends := make([]*lb.Backend, 0, 1+len(remoteBackends)) 1580 allBackends = append(allBackends, localBackend) 1581 allBackends = append(allBackends, remoteBackends...) 1582 1583 p1 := &lb.SVC{ 1584 Frontend: frontend1, 1585 Backends: allBackends, 1586 Type: lb.SVCTypeClusterIP, 1587 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1588 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1589 Name: lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"}, 1590 } 1591 1592 // Insert the service entry of type ClusterIP. 1593 created, id, err := m.svc.UpsertService(p1) 1594 require.Nil(t, err) 1595 require.Equal(t, true, created) 1596 require.NotEqual(t, lb.ID(0), id) 1597 1598 svc, ok := m.svc.svcByID[id] 1599 require.Equal(t, len(allBackends), len(svc.backends)) 1600 require.Equal(t, true, ok) 1601 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1602 1603 echoOtherNode := lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"} 1604 resource1 := L7LBResourceName{Name: "testOwner1", Namespace: "cilium-test"} 1605 1606 // Registering with redirection stores the proxy port. 1607 resource2 := L7LBResourceName{Name: "testOwner2", Namespace: "cilium-test"} 1608 err = m.svc.RegisterL7LBServiceRedirect(echoOtherNode, resource2, 9090, []uint16{80}) 1609 require.Nil(t, err) 1610 1611 svc, ok = m.svc.svcByID[id] 1612 require.Equal(t, len(allBackends), len(svc.backends)) 1613 require.Equal(t, true, ok) 1614 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1615 1616 // removing the registration with redirection removes the proxy port 1617 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource2) 1618 require.Nil(t, err) 1619 1620 svc, ok = m.svc.svcByID[id] 1621 require.Equal(t, len(allBackends), len(svc.backends)) 1622 require.Equal(t, true, ok) 1623 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1624 1625 // Registering with non-matching port does not store the proxy port. 1626 err = m.svc.RegisterL7LBServiceRedirect(echoOtherNode, resource2, 9090, []uint16{8080}) 1627 require.Nil(t, err) 1628 1629 svc, ok = m.svc.svcByID[id] 1630 require.Equal(t, len(allBackends), len(svc.backends)) 1631 require.Equal(t, true, ok) 1632 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1633 1634 // registering redirection for a Service that already has a redirect registration 1635 // should result in an error. 1636 resource3 := L7LBResourceName{Name: "testOwner3", Namespace: "cilium-test"} 1637 err = m.svc.RegisterL7LBServiceRedirect(echoOtherNode, resource3, 10000, nil) 1638 require.Error(t, err) 1639 1640 // Adding a matching frontend gets proxy port 1641 1642 p2 := &lb.SVC{ 1643 Frontend: frontend1_8080, 1644 Backends: allBackends, 1645 Type: lb.SVCTypeClusterIP, 1646 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1647 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1648 Name: lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"}, 1649 } 1650 1651 // Insert the service entry of type ClusterIP. 1652 created, id2, err := m.svc.UpsertService(p2) 1653 require.Nil(t, err) 1654 require.Equal(t, true, created) 1655 require.NotEqual(t, lb.ID(0), id2) 1656 1657 svc, ok = m.svc.svcByID[id2] 1658 require.Equal(t, len(allBackends), len(svc.backends)) 1659 require.Equal(t, true, ok) 1660 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1661 1662 // Remove with an unregistered owner name does not remove 1663 resource4 := L7LBResourceName{Name: "testOwner4", Namespace: "cilium-test"} 1664 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource4) 1665 require.Nil(t, err) 1666 1667 svc, ok = m.svc.svcByID[id2] 1668 require.Equal(t, len(allBackends), len(svc.backends)) 1669 require.Equal(t, true, ok) 1670 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1671 1672 // Removing registration without redirection does not remove the proxy port 1673 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource1) 1674 require.Nil(t, err) 1675 1676 svc, ok = m.svc.svcByID[id2] 1677 require.Equal(t, len(allBackends), len(svc.backends)) 1678 require.Equal(t, true, ok) 1679 require.Equal(t, uint16(9090), svc.l7LBProxyPort) 1680 1681 // removing the registration with redirection removes the proxy port 1682 err = m.svc.DeregisterL7LBServiceRedirect(echoOtherNode, resource2) 1683 require.Nil(t, err) 1684 1685 svc, ok = m.svc.svcByID[id] 1686 require.Equal(t, len(allBackends), len(svc.backends)) 1687 require.Equal(t, true, ok) 1688 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1689 1690 svc, ok = m.svc.svcByID[id2] 1691 require.Equal(t, len(allBackends), len(svc.backends)) 1692 require.Equal(t, true, ok) 1693 require.Equal(t, uint16(0), svc.l7LBProxyPort) 1694 } 1695 1696 // L7 LB proxies should be able to register callback based backend sync registration 1697 func TestL7LoadBalancerServiceBackendSyncRegistration(t *testing.T) { 1698 m := setupManagerTestSuite(t) 1699 1700 // Create a node-local backend. 1701 localBackend := backends1[0] 1702 localBackend.NodeName = nodeTypes.GetName() 1703 // Create two remote backends. 1704 remoteBackends := make([]*lb.Backend, 0, len(backends2)) 1705 for _, backend := range backends2 { 1706 backend.NodeName = "not-" + nodeTypes.GetName() 1707 remoteBackends = append(remoteBackends, backend) 1708 } 1709 allBackends := make([]*lb.Backend, 0, 1+len(remoteBackends)) 1710 allBackends = append(allBackends, localBackend) 1711 allBackends = append(allBackends, remoteBackends...) 1712 1713 p1 := &lb.SVC{ 1714 Frontend: frontend1, 1715 Backends: allBackends, 1716 Type: lb.SVCTypeClusterIP, 1717 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1718 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1719 Name: lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"}, 1720 } 1721 1722 // Insert the service entry of type ClusterIP. 1723 created, id, err := m.svc.UpsertService(p1) 1724 require.Nil(t, err) 1725 require.Equal(t, true, created) 1726 require.NotEqual(t, lb.ID(0), id) 1727 1728 // Registering L7LB backend sync should register backend sync and trigger an initial synchronization 1729 service := lb.ServiceName{Name: "echo-other-node", Namespace: "cilium-test"} 1730 backendSyncer := &FakeBackendSyncer{} 1731 err = m.svc.RegisterL7LBServiceBackendSync(service, backendSyncer) 1732 require.Nil(t, err) 1733 1734 require.Equal(t, 1, len(m.svc.l7lbSvcs)) 1735 require.Equal(t, 1, len(m.svc.l7lbSvcs[service].backendSyncRegistrations)) 1736 require.Equal(t, len(allBackends), backendSyncer.nrOfBackends) 1737 require.Equal(t, 1, backendSyncer.nrOfSyncs) 1738 1739 // Re-Registering L7LB backend sync should keep the existing registration and trigger an implicit re-synchronization 1740 err = m.svc.RegisterL7LBServiceBackendSync(service, backendSyncer) 1741 require.Nil(t, err) 1742 1743 require.Equal(t, 1, len(m.svc.l7lbSvcs)) 1744 require.Equal(t, 1, len(m.svc.l7lbSvcs[service].backendSyncRegistrations)) 1745 require.Equal(t, len(allBackends), backendSyncer.nrOfBackends) 1746 require.Equal(t, 2, backendSyncer.nrOfSyncs) 1747 1748 // Upserting a service should trigger a sync for the registered backend sync registrations 1749 allBackends = append(allBackends, backends4...) 1750 p1.Backends = allBackends 1751 created, id, err = m.svc.UpsertService(p1) 1752 require.Nil(t, err) 1753 require.Equal(t, false, created) 1754 require.NotEqual(t, lb.ID(0), id) 1755 1756 require.Equal(t, 1, len(m.svc.l7lbSvcs)) 1757 require.Equal(t, 1, len(m.svc.l7lbSvcs[service].backendSyncRegistrations)) 1758 require.Equal(t, len(allBackends), backendSyncer.nrOfBackends) 1759 require.Equal(t, 3, backendSyncer.nrOfSyncs) 1760 1761 // De-registering a backend sync should delete the backend sync registration 1762 err = m.svc.DeregisterL7LBServiceBackendSync(service, backendSyncer) 1763 require.Nil(t, err) 1764 1765 require.Equal(t, 0, len(m.svc.l7lbSvcs)) 1766 require.Equal(t, len(allBackends), backendSyncer.nrOfBackends) 1767 require.Equal(t, 3, backendSyncer.nrOfSyncs) 1768 } 1769 1770 // Tests that services with the given backends are updated with the new backend 1771 // state. 1772 func TestUpdateBackendsState(t *testing.T) { 1773 m := setupManagerTestSuite(t) 1774 1775 backends := make([]*lb.Backend, 0, len(backends1)) 1776 for _, b := range backends1 { 1777 backends = append(backends, b.DeepCopy()) 1778 } 1779 backends[0].State = lb.BackendStateActive 1780 backends[1].State = lb.BackendStateActive 1781 p1 := &lb.SVC{ 1782 Frontend: frontend1, 1783 Backends: backends, 1784 Type: lb.SVCTypeClusterIP, 1785 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 1786 } 1787 p2 := &lb.SVC{ 1788 Frontend: frontend2, 1789 Backends: backends, 1790 Type: lb.SVCTypeClusterIP, 1791 Name: lb.ServiceName{Name: "svc2", Namespace: "ns1"}, 1792 } 1793 1794 _, id1, err1 := m.svc.UpsertService(p1) 1795 _, id2, err2 := m.svc.UpsertService(p2) 1796 1797 require.Nil(t, err1) 1798 require.Nil(t, err2) 1799 require.Equal(t, lb.ID(1), id1) 1800 require.Equal(t, lb.ID(2), id2) 1801 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id1].backends[0].State) 1802 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id1].backends[1].State) 1803 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id2].backends[0].State) 1804 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id2].backends[1].State) 1805 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1806 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 1807 require.Equal(t, len(backends), m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1808 require.Equal(t, len(backends), m.lbmap.SvcActiveBackendsCount[uint16(id2)]) 1809 require.Equal(t, len(backends), len(m.lbmap.BackendByID)) 1810 // Backend states are persisted in the map. 1811 require.Equal(t, lb.BackendStateActive, m.lbmap.BackendByID[1].State) 1812 require.Equal(t, lb.BackendStateActive, m.lbmap.BackendByID[2].State) 1813 1814 // Update the state for one of the backends. 1815 updated := []*lb.Backend{backends[0]} 1816 updated[0].State = lb.BackendStateQuarantined 1817 1818 err := m.svc.UpdateBackendsState(updated) 1819 1820 require.Nil(t, err) 1821 // Both the services are updated with the update backend state. 1822 require.Equal(t, lb.BackendStateQuarantined, m.svc.svcByID[id1].backends[0].State) 1823 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id1].backends[1].State) 1824 require.Equal(t, lb.BackendStateQuarantined, m.svc.svcByID[id2].backends[0].State) 1825 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id2].backends[1].State) 1826 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1827 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 1828 require.Equal(t, 1, m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1829 require.Equal(t, 1, m.lbmap.SvcActiveBackendsCount[uint16(id2)]) 1830 require.Equal(t, len(backends), len(m.lbmap.BackendByID)) 1831 // Updated backend states are persisted in the map. 1832 require.Equal(t, lb.BackendStateQuarantined, m.lbmap.BackendByID[1].State) 1833 require.Equal(t, lb.BackendStateActive, m.lbmap.BackendByID[2].State) 1834 1835 // Update the state again. 1836 updated = []*lb.Backend{backends[0]} 1837 updated[0].State = lb.BackendStateActive 1838 1839 err = m.svc.UpdateBackendsState(updated) 1840 1841 require.Nil(t, err) 1842 // Both the services are updated with the update backend state. 1843 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id1].backends[0].State) 1844 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id1].backends[1].State) 1845 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id2].backends[0].State) 1846 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id2].backends[1].State) 1847 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1848 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id2)].Backends)) 1849 require.Equal(t, len(backends), m.lbmap.SvcActiveBackendsCount[uint16(id1)]) 1850 require.Equal(t, len(backends), m.lbmap.SvcActiveBackendsCount[uint16(id2)]) 1851 require.Equal(t, len(backends), len(m.lbmap.BackendByID)) 1852 // Updated backend states are persisted in the map. 1853 require.Equal(t, lb.BackendStateActive, m.lbmap.BackendByID[1].State) 1854 require.Equal(t, lb.BackendStateActive, m.lbmap.BackendByID[2].State) 1855 } 1856 1857 // Tests that backend states are restored. 1858 func TestRestoreServiceWithBackendStates(t *testing.T) { 1859 m := setupManagerTestSuite(t) 1860 1861 option.Config.NodePortAlg = option.NodePortAlgMaglev 1862 bs := append(backends1, backends4...) 1863 backends := make([]*lb.Backend, 0, len(bs)) 1864 for _, b := range bs { 1865 backends = append(backends, b.DeepCopy()) 1866 } 1867 backends[0].State = lb.BackendStateActive 1868 backends[1].State = lb.BackendStateActive 1869 backends[2].State = lb.BackendStateActive 1870 1871 p1 := &lb.SVC{ 1872 Frontend: frontend1, 1873 Backends: backends, 1874 SessionAffinity: true, 1875 SessionAffinityTimeoutSec: 100, 1876 Type: lb.SVCTypeNodePort, 1877 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1878 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1879 } 1880 created, id1, err := m.svc.UpsertService(p1) 1881 1882 require.Nil(t, err) 1883 require.Equal(t, true, created) 1884 require.Equal(t, lb.ID(1), id1) 1885 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1886 require.Equal(t, len(backends), len(m.svc.backendByHash)) 1887 1888 // Update backend states. 1889 var updates []*lb.Backend 1890 backends[0].State = lb.BackendStateQuarantined 1891 backends[1].State = lb.BackendStateMaintenance 1892 updates = append(updates, backends[0], backends[1]) 1893 err = m.svc.UpdateBackendsState(updates) 1894 1895 require.Nil(t, err) 1896 1897 // Simulate agent restart. 1898 lbmap := m.svc.lbmap.(*mockmaps.LBMockMap) 1899 m.newServiceMock(lbmap) 1900 1901 // Restore services from lbmap 1902 err = m.svc.RestoreServices() 1903 require.Nil(t, err) 1904 1905 // Check that backends along with their states have been restored 1906 require.Equal(t, len(backends), len(m.svc.backendByHash)) 1907 statesMatched := 0 1908 for _, b := range backends { 1909 be, found := m.svc.backendByHash[b.Hash()] 1910 require.Equal(t, true, found) 1911 if be.String() == b.String() { 1912 require.Equal(t, b.State, be.State, "before %+v restored %+v", b, be) 1913 statesMatched++ 1914 } 1915 } 1916 require.Equal(t, len(backends), statesMatched) 1917 require.Equal(t, 1, m.lbmap.DummyMaglevTable[uint16(id1)]) 1918 } 1919 1920 func TestUpsertServiceWithZeroWeightBackends(t *testing.T) { 1921 m := setupManagerTestSuite(t) 1922 1923 option.Config.NodePortAlg = option.NodePortAlgMaglev 1924 backends := append(backends1, backends4...) 1925 backends[1].Weight = 0 1926 backends[1].State = lb.BackendStateMaintenance 1927 backends[2].Weight = 1 1928 1929 p := &lb.SVC{ 1930 Frontend: frontend1, 1931 Backends: backends, 1932 Type: lb.SVCTypeNodePort, 1933 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 1934 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 1935 SessionAffinity: true, 1936 SessionAffinityTimeoutSec: 100, 1937 Name: lb.ServiceName{ 1938 Name: "svc1", 1939 Namespace: "ns1", 1940 }, 1941 } 1942 1943 created, id1, err := m.svc.UpsertService(p) 1944 1945 require.Nil(t, err) 1946 require.Equal(t, true, created) 1947 require.Equal(t, 3, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1948 require.Equal(t, 3, len(m.lbmap.BackendByID)) 1949 hash := backends[1].L3n4Addr.Hash() 1950 require.Equal(t, lb.BackendStateMaintenance, m.svc.backendByHash[hash].State) 1951 require.Equal(t, lb.BackendStateMaintenance, m.svc.svcByID[id1].backendByHash[hash].State) 1952 hash2 := backends[2].L3n4Addr.Hash() 1953 require.Equal(t, lb.BackendStateActive, m.svc.backendByHash[hash2].State) 1954 require.Equal(t, lb.BackendStateActive, m.svc.svcByID[id1].backendByHash[hash2].State) 1955 require.Equal(t, 2, m.lbmap.DummyMaglevTable[uint16(id1)]) 1956 1957 // Update existing backend weight 1958 p.Backends[2].Weight = 0 1959 p.Backends[2].State = lb.BackendStateMaintenance 1960 1961 created, id1, err = m.svc.UpsertService(p) 1962 1963 require.Nil(t, err) 1964 require.Equal(t, false, created) 1965 require.Equal(t, 3, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1966 require.Equal(t, 3, len(m.lbmap.BackendByID)) 1967 require.Equal(t, lb.BackendStateMaintenance, m.svc.svcByID[id1].backendByHash[hash2].State) 1968 require.Equal(t, 1, m.lbmap.DummyMaglevTable[uint16(id1)]) 1969 1970 // Delete backends with weight 0 1971 p.Backends = backends[:1] 1972 1973 created, id1, err = m.svc.UpsertService(p) 1974 1975 require.Nil(t, err) 1976 require.Equal(t, false, created) 1977 require.Equal(t, 1, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 1978 require.Equal(t, 1, len(m.lbmap.BackendByID)) 1979 require.Equal(t, 1, m.lbmap.DummyMaglevTable[uint16(id1)]) 1980 } 1981 1982 func TestUpdateBackendsStateWithBackendSharedAcrossServices(t *testing.T) { 1983 m := setupManagerTestSuite(t) 1984 1985 option.Config.NodePortAlg = option.NodePortAlgMaglev 1986 be := append(backends1, backends4...) 1987 backends := make([]*lb.Backend, 0, len(be)) 1988 for _, b := range be { 1989 backends = append(backends, b.DeepCopy()) 1990 } 1991 backends[0].State = lb.BackendStateActive 1992 backends[1].State = lb.BackendStateActive 1993 backends[2].State = lb.BackendStateMaintenance 1994 hash0 := backends[0].L3n4Addr.Hash() 1995 hash1 := backends[1].L3n4Addr.Hash() 1996 hash2 := backends[2].L3n4Addr.Hash() 1997 1998 p := &lb.SVC{ 1999 Frontend: frontend1, 2000 Backends: backends, 2001 Type: lb.SVCTypeNodePort, 2002 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 2003 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 2004 SessionAffinity: true, 2005 SessionAffinityTimeoutSec: 100, 2006 Name: lb.ServiceName{ 2007 Name: "svc1", 2008 Namespace: "ns1", 2009 }, 2010 } 2011 r := &lb.SVC{ 2012 Frontend: frontend2, 2013 Backends: backends, 2014 Type: lb.SVCTypeNodePort, 2015 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 2016 IntTrafficPolicy: lb.SVCTrafficPolicyCluster, 2017 SessionAffinity: true, 2018 SessionAffinityTimeoutSec: 100, 2019 Name: lb.ServiceName{ 2020 Name: "svc2", 2021 Namespace: "ns1", 2022 }, 2023 } 2024 svcHash2 := r.Frontend.Hash() 2025 2026 _, _, err := m.svc.UpsertService(p) 2027 require.Nil(t, err) 2028 _, _, err = m.svc.UpsertService(r) 2029 require.Nil(t, err) 2030 _, id1, err := m.svc.UpsertService(r) 2031 2032 // Assert expected backend states after consecutive upsert service calls that share the backends. 2033 require.Nil(t, err) 2034 require.Equal(t, 3, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 2035 require.Equal(t, 3, len(m.lbmap.BackendByID)) 2036 require.Equal(t, lb.BackendStateActive, m.svc.backendByHash[hash0].State) 2037 require.Equal(t, lb.BackendStateActive, m.svc.backendByHash[hash1].State) 2038 require.Equal(t, lb.BackendStateMaintenance, m.svc.backendByHash[hash2].State) 2039 2040 backends[1].State = lb.BackendStateMaintenance 2041 err = m.svc.UpdateBackendsState(backends) 2042 2043 require.Nil(t, err) 2044 require.Equal(t, lb.BackendStateMaintenance, m.svc.backendByHash[hash1].State) 2045 require.Equal(t, lb.BackendStateMaintenance, m.svc.svcByHash[svcHash2].backends[1].State) 2046 require.Equal(t, lb.BackendStateMaintenance, m.svc.svcByHash[svcHash2].backendByHash[hash1].State) 2047 } 2048 2049 func TestSyncNodePortFrontends(t *testing.T) { 2050 m := setupManagerTestSuite(t) 2051 2052 // Add a IPv4 surrogate frontend 2053 surrogate := &lb.SVC{ 2054 Frontend: surrogateFE, 2055 Backends: backends1, 2056 Type: lb.SVCTypeNodePort, 2057 } 2058 _, surrID, err := m.svc.UpsertService(surrogate) 2059 require.Nil(t, err) 2060 p1 := &lb.SVC{ 2061 Frontend: frontend1, 2062 Backends: backends1, 2063 Type: lb.SVCTypeNodePort, 2064 } 2065 _, _, err = m.svc.UpsertService(p1) 2066 require.Nil(t, err) 2067 require.Equal(t, 2, len(m.svc.svcByID)) 2068 2069 // With no addresses all frontends (except surrogates) should be removed. 2070 err = m.svc.SyncNodePortFrontends(sets.New[netip.Addr]()) 2071 require.Nil(t, err) 2072 2073 require.Equal(t, 1, len(m.svc.svcByID)) 2074 _, ok := m.svc.svcByID[surrID] 2075 require.Equal(t, true, ok) 2076 2077 // With a new frontend addresses services should be re-created. 2078 nodeAddrs := sets.New[netip.Addr]( 2079 frontend1.AddrCluster.Addr(), 2080 frontend2.AddrCluster.Addr(), 2081 // IPv6 address should be ignored initially without IPv6 surrogate 2082 frontend3.AddrCluster.Addr(), 2083 ) 2084 m.svc.SyncNodePortFrontends(nodeAddrs) 2085 require.Equal(t, 2+1 /* surrogate */, len(m.svc.svcByID)) 2086 2087 _, _, found := m.svc.GetServiceNameByAddr(frontend1.L3n4Addr) 2088 require.Equal(t, true, found) 2089 _, _, found = m.svc.GetServiceNameByAddr(frontend2.L3n4Addr) 2090 require.Equal(t, true, found) 2091 2092 // Add an IPv6 surrogate 2093 surrogate = &lb.SVC{ 2094 Frontend: surrogateFEv6, 2095 Backends: backends3, 2096 Type: lb.SVCTypeNodePort, 2097 } 2098 _, _, err = m.svc.UpsertService(surrogate) 2099 require.Nil(t, err) 2100 2101 err = m.svc.SyncNodePortFrontends(nodeAddrs) 2102 require.Nil(t, err) 2103 require.Equal(t, 3+2 /* surrogates */, len(m.svc.svcByID)) 2104 } 2105 2106 func TestTrafficPolicy(t *testing.T) { 2107 m := setupManagerTestSuite(t) 2108 2109 internalIP := *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.1"), 80, lb.ScopeInternal, 0) 2110 externalIP := *lb.NewL3n4AddrID(lb.TCP, cmtypes.MustParseAddrCluster("1.1.1.1"), 80, lb.ScopeExternal, 0) 2111 2112 localBackend1 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.1"), 8080) 2113 localBackend2 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.2"), 8080) 2114 localBackend1.NodeName = nodeTypes.GetName() 2115 localBackend2.NodeName = nodeTypes.GetName() 2116 localBackends := []*lb.Backend{localBackend1, localBackend2} 2117 2118 remoteBackend1 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.3"), 8080) 2119 remoteBackend2 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.4"), 8080) 2120 remoteBackend3 := lb.NewBackend(0, lb.TCP, cmtypes.MustParseAddrCluster("10.0.0.5"), 8080) 2121 remoteBackend1.NodeName = "not-" + nodeTypes.GetName() 2122 remoteBackend2.NodeName = "not-" + nodeTypes.GetName() 2123 remoteBackend3.NodeName = "not-" + nodeTypes.GetName() 2124 remoteBackends := []*lb.Backend{remoteBackend1, remoteBackend2, remoteBackend3} 2125 2126 allBackends := make([]*lb.Backend, 0, len(remoteBackends)+len(remoteBackends)) 2127 allBackends = append(allBackends, localBackends...) 2128 allBackends = append(allBackends, remoteBackends...) 2129 2130 p1 := &lb.SVC{ 2131 Frontend: internalIP, 2132 Backends: allBackends, 2133 Type: lb.SVCTypeLoadBalancer, 2134 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 2135 IntTrafficPolicy: lb.SVCTrafficPolicyLocal, 2136 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 2137 } 2138 created, id1, err := m.svc.UpsertService(p1) 2139 require.Equal(t, true, created) 2140 require.Nil(t, err) 2141 2142 p2 := &lb.SVC{ 2143 Frontend: externalIP, 2144 Backends: allBackends, 2145 Type: lb.SVCTypeLoadBalancer, 2146 ExtTrafficPolicy: lb.SVCTrafficPolicyCluster, 2147 IntTrafficPolicy: lb.SVCTrafficPolicyLocal, 2148 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 2149 } 2150 created, id2, err := m.svc.UpsertService(p2) 2151 require.Equal(t, true, created) 2152 require.Nil(t, err) 2153 2154 svcFromLbMap1, ok := m.lbmap.ServiceByID[uint16(id1)] 2155 require.Equal(t, true, ok) 2156 require.Equal(t, len(localBackends), len(svcFromLbMap1.Backends)) 2157 2158 svcFromLbMap2, ok := m.lbmap.ServiceByID[uint16(id2)] 2159 require.Equal(t, true, ok) 2160 require.Equal(t, len(allBackends), len(svcFromLbMap2.Backends)) 2161 2162 p1.ExtTrafficPolicy = lb.SVCTrafficPolicyLocal 2163 p1.IntTrafficPolicy = lb.SVCTrafficPolicyCluster 2164 created, id3, err := m.svc.UpsertService(p1) 2165 require.Equal(t, false, created) 2166 require.Nil(t, err) 2167 require.Equal(t, id1, id3) 2168 2169 svcFromLbMap3, ok := m.lbmap.ServiceByID[uint16(id1)] 2170 require.Equal(t, true, ok) 2171 require.Equal(t, len(allBackends), len(svcFromLbMap3.Backends)) 2172 2173 p2.ExtTrafficPolicy = lb.SVCTrafficPolicyLocal 2174 p2.IntTrafficPolicy = lb.SVCTrafficPolicyCluster 2175 created, id4, err := m.svc.UpsertService(p2) 2176 require.Equal(t, false, created) 2177 require.Nil(t, err) 2178 require.Equal(t, id2, id4) 2179 2180 svcFromLbMap4, ok := m.lbmap.ServiceByID[uint16(id2)] 2181 require.Equal(t, true, ok) 2182 require.Equal(t, len(localBackends), len(svcFromLbMap4.Backends)) 2183 2184 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 2185 require.Nil(t, err) 2186 require.Equal(t, true, found) 2187 found, err = m.svc.DeleteServiceByID(lb.ServiceID(id2)) 2188 require.Nil(t, err) 2189 require.Equal(t, true, found) 2190 } 2191 2192 // Tests whether delete service handles non-active backends. 2193 func TestDeleteServiceWithTerminatingBackends(t *testing.T) { 2194 m := setupManagerTestSuite(t) 2195 2196 backends := backends5 2197 backends[0].State = lb.BackendStateTerminating 2198 p := &lb.SVC{ 2199 Frontend: frontend1, 2200 Backends: backends, 2201 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 2202 } 2203 2204 created, id1, err := m.svc.UpsertService(p) 2205 2206 require.Nil(t, err) 2207 require.Equal(t, true, created) 2208 require.Equal(t, lb.ID(1), id1) 2209 require.Equal(t, 2, len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 2210 require.Equal(t, 2, len(m.lbmap.BackendByID)) 2211 require.Equal(t, lb.ServiceName{Name: "svc1", Namespace: "ns1"}, m.svc.svcByID[id1].svcName) 2212 2213 // Delete service. 2214 found, err := m.svc.DeleteServiceByID(lb.ServiceID(id1)) 2215 2216 require.Nil(t, err) 2217 require.Equal(t, true, found) 2218 require.Equal(t, 0, len(m.lbmap.ServiceByID)) 2219 require.Equal(t, 0, len(m.lbmap.BackendByID)) 2220 } 2221 2222 func TestRestoreServicesWithLeakedBackends(t *testing.T) { 2223 m := setupManagerTestSuite(t) 2224 2225 backends := make([]*lb.Backend, len(backends1)) 2226 backends[0] = backends1[0].DeepCopy() 2227 backends[1] = backends1[1].DeepCopy() 2228 p1 := &lb.SVC{ 2229 Frontend: frontend1, 2230 Backends: backends, 2231 Type: lb.SVCTypeClusterIP, 2232 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 2233 } 2234 2235 _, id1, err1 := m.svc.UpsertService(p1) 2236 2237 require.Nil(t, err1) 2238 require.Equal(t, lb.ID(1), id1) 2239 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 2240 require.Equal(t, len(backends), len(m.lbmap.BackendByID)) 2241 2242 // Simulate leaked backends with various leaked scenarios. 2243 // Backend2 is a duplicate leaked backend with the same L3nL4Addr as backends[0] 2244 // that's associated with the service. 2245 // Backend3 is a leaked backend with no associated service. 2246 // Backend4 and Backend5 are duplicate leaked backends with no associated service. 2247 backend2 := backends[0].DeepCopy() 2248 backend2.ID = lb.BackendID(10) 2249 backend3 := backends2[0].DeepCopy() 2250 backend4 := backends6[0].DeepCopy() 2251 backend4.ID = lb.BackendID(20) 2252 backend5 := backends6[0].DeepCopy() 2253 backend5.ID = lb.BackendID(30) 2254 m.svc.lbmap.AddBackend(backend2, backend2.L3n4Addr.IsIPv6()) 2255 m.svc.lbmap.AddBackend(backend3, backend3.L3n4Addr.IsIPv6()) 2256 m.svc.lbmap.AddBackend(backend4, backend4.L3n4Addr.IsIPv6()) 2257 m.svc.lbmap.AddBackend(backend5, backend5.L3n4Addr.IsIPv6()) 2258 require.Equal(t, len(backends)+4, len(m.lbmap.BackendByID)) 2259 lbmap := m.svc.lbmap.(*mockmaps.LBMockMap) 2260 m.svc = newService(&FakeMonitorAgent{}, lbmap, nil) 2261 2262 // Restore services from lbmap 2263 err := m.svc.RestoreServices() 2264 require.Nil(t, err) 2265 require.Equal(t, len(backends), len(m.lbmap.ServiceByID[uint16(id1)].Backends)) 2266 // Leaked backends should be deleted. 2267 require.Equal(t, len(backends), len(m.lbmap.BackendByID)) 2268 } 2269 2270 // Tests backend connections getting destroyed. 2271 func TestUpsertServiceWithDeletedBackends(t *testing.T) { 2272 m := setupManagerTestSuite(t) 2273 2274 option.Config.EnableSocketLB = true 2275 backends := []*lb.Backend{ 2276 lb.NewBackend(0, lb.UDP, cmtypes.MustParseAddrCluster("10.0.0.1"), 8080), 2277 lb.NewBackend(0, lb.UDP, cmtypes.MustParseAddrCluster("10.0.0.2"), 8080), 2278 } 2279 cookie1 := [2]uint32{1234, 0} 2280 cookie2 := [2]uint32{1235, 0} 2281 id1 := netlink.SocketID{ 2282 DestinationPort: 8080, 2283 Destination: backends[0].L3n4Addr.AddrCluster.Addr().AsSlice(), 2284 Cookie: cookie1, 2285 } 2286 id2 := netlink.SocketID{ 2287 DestinationPort: 8080, 2288 Destination: backends[1].L3n4Addr.AddrCluster.Addr().AsSlice(), 2289 Cookie: cookie2, 2290 } 2291 // Socket connected to backend1 2292 s1 := testsockets.MockSocket{ 2293 SockID: id1, Family: syscall.AF_INET, Protocol: unix.IPPROTO_UDP, 2294 } 2295 // Socket connected to backend2 2296 s2 := testsockets.MockSocket{ 2297 SockID: id2, Family: syscall.AF_INET, Protocol: unix.IPPROTO_UDP, 2298 } 2299 svc := &lb.SVC{ 2300 Frontend: frontend1, 2301 Backends: backends, 2302 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 2303 } 2304 key1 := *lbmap.NewSockRevNat4Key(1234, s1.SockID.Destination, s1.SockID.DestinationPort) 2305 key2 := *lbmap.NewSockRevNat4Key(1235, s2.SockID.Destination, s2.SockID.DestinationPort) 2306 m.lbmap.SockRevNat4[key1] = lbmap.SockRevNat4Value{} 2307 m.lbmap.SockRevNat4[key2] = lbmap.SockRevNat4Value{} 2308 sockets := []*testsockets.MockSocket{&s1, &s2} 2309 m.svc.backendConnectionHandler = testsockets.NewMockSockets(sockets) 2310 2311 created, _, err := m.svc.UpsertService(svc) 2312 2313 require.Nil(t, err) 2314 require.Equal(t, true, created) 2315 2316 // Delete one of the backends. 2317 svc = &lb.SVC{ 2318 Frontend: frontend1, 2319 Backends: []*lb.Backend{backends[1]}, 2320 Name: lb.ServiceName{Name: "svc1", Namespace: "ns1"}, 2321 } 2322 2323 created, _, err = m.svc.UpsertService(svc) 2324 2325 require.Nil(t, err) 2326 require.Equal(t, false, created) 2327 2328 // Only the sockets connected to the deleted backend are destroyed. 2329 for _, socket := range sockets { 2330 if socket.Equal(sockets[0]) { 2331 require.Equal(t, true, socket.Destroyed) 2332 } else { 2333 require.Equal(t, false, socket.Destroyed) 2334 } 2335 } 2336 } 2337 2338 type FakeBackendSyncer struct { 2339 nrOfBackends int 2340 nrOfSyncs int 2341 } 2342 2343 var _ BackendSyncer = &FakeBackendSyncer{} 2344 2345 func (r *FakeBackendSyncer) ProxyName() string { 2346 return "Fake" 2347 } 2348 2349 func (r *FakeBackendSyncer) Sync(svc *lb.SVC) error { 2350 r.nrOfBackends = len(svc.Backends) 2351 r.nrOfSyncs++ 2352 2353 return nil 2354 } 2355 2356 type FakeMonitorAgent struct{} 2357 2358 var _ monitorAgent.Agent = &FakeMonitorAgent{} 2359 2360 func (f *FakeMonitorAgent) AttachToEventsMap(nPages int) error { 2361 return nil 2362 } 2363 2364 func (f *FakeMonitorAgent) RegisterNewConsumer(newConsumer consumer.MonitorConsumer) { 2365 } 2366 2367 func (f *FakeMonitorAgent) RegisterNewListener(newListener listener.MonitorListener) { 2368 } 2369 2370 func (f *FakeMonitorAgent) RemoveConsumer(mc consumer.MonitorConsumer) { 2371 } 2372 2373 func (f *FakeMonitorAgent) RemoveListener(ml listener.MonitorListener) { 2374 } 2375 2376 func (f *FakeMonitorAgent) SendEvent(typ int, event interface{}) error { 2377 return nil 2378 } 2379 2380 func (f *FakeMonitorAgent) State() *models.MonitorStatus { 2381 return nil 2382 }