github.com/cilium/cilium@v1.16.2/pkg/k8s/service_cache_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package k8s
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"net"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/cilium/hive/hivetest"
    14  	"github.com/cilium/statedb"
    15  	"github.com/cilium/stream"
    16  	"github.com/stretchr/testify/require"
    17  	v1 "k8s.io/api/core/v1"
    18  
    19  	cmtypes "github.com/cilium/cilium/pkg/clustermesh/types"
    20  	datapathTables "github.com/cilium/cilium/pkg/datapath/tables"
    21  	slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1"
    22  	slim_discovery_v1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/discovery/v1"
    23  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    24  	"github.com/cilium/cilium/pkg/loadbalancer"
    25  	"github.com/cilium/cilium/pkg/lock"
    26  	"github.com/cilium/cilium/pkg/node"
    27  	"github.com/cilium/cilium/pkg/node/types"
    28  	"github.com/cilium/cilium/pkg/option"
    29  	serviceStore "github.com/cilium/cilium/pkg/service/store"
    30  	"github.com/cilium/cilium/pkg/testutils"
    31  )
    32  
    33  func newDB(t *testing.T) (*statedb.DB, statedb.RWTable[datapathTables.NodeAddress]) {
    34  	db := statedb.New()
    35  	nodeAddrs, err := datapathTables.NewNodeAddressTable()
    36  	require.NoError(t, err)
    37  
    38  	err = db.RegisterTable(nodeAddrs)
    39  	require.NoError(t, err)
    40  
    41  	txn := db.WriteTxn(nodeAddrs)
    42  	for _, addr := range datapathTables.TestAddresses {
    43  		nodeAddrs.Insert(txn, addr)
    44  	}
    45  	txn.Commit()
    46  
    47  	return db, nodeAddrs
    48  }
    49  
    50  func TestGetUniqueServiceFrontends(t *testing.T) {
    51  	svcID1 := ServiceID{Name: "svc1", Namespace: "default"}
    52  	svcID2 := ServiceID{Name: "svc2", Namespace: "default"}
    53  
    54  	endpoints := Endpoints{
    55  		Backends: map[cmtypes.AddrCluster]*Backend{
    56  			cmtypes.MustParseAddrCluster("3.3.3.3"): {
    57  				Ports: map[string]*loadbalancer.L4Addr{
    58  					"port": {
    59  						Protocol: loadbalancer.TCP,
    60  						Port:     80,
    61  					},
    62  				},
    63  			},
    64  		},
    65  	}
    66  
    67  	db, nodeAddrs := newDB(t)
    68  	cache := NewServiceCache(db, nodeAddrs)
    69  
    70  	cache.services = map[ServiceID]*Service{
    71  		svcID1: {
    72  			FrontendIPs: []net.IP{net.ParseIP("1.1.1.1")},
    73  			Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
    74  				loadbalancer.FEPortName("foo"): {
    75  					Protocol: loadbalancer.TCP,
    76  					Port:     10,
    77  				},
    78  				loadbalancer.FEPortName("bar"): {
    79  					Protocol: loadbalancer.TCP,
    80  					Port:     20,
    81  				},
    82  			},
    83  		},
    84  		svcID2: {
    85  			FrontendIPs: []net.IP{net.ParseIP("2.2.2.2")},
    86  			Ports: map[loadbalancer.FEPortName]*loadbalancer.L4Addr{
    87  				loadbalancer.FEPortName("bar"): {
    88  					Protocol: loadbalancer.UDP,
    89  					Port:     20,
    90  				},
    91  			},
    92  		},
    93  	}
    94  	cache.endpoints = map[ServiceID]*EndpointSlices{
    95  		svcID1: {
    96  			epSlices: map[string]*Endpoints{
    97  				"": &endpoints,
    98  			},
    99  		},
   100  		svcID2: {
   101  			epSlices: map[string]*Endpoints{
   102  				"": &endpoints,
   103  			},
   104  		},
   105  	}
   106  
   107  	frontends := cache.UniqueServiceFrontends()
   108  	require.EqualValues(t, FrontendList{
   109  		"1.1.1.1:10/TCP": {},
   110  		"1.1.1.1:20/TCP": {},
   111  		"2.2.2.2:20/UDP": {},
   112  	}, frontends)
   113  
   114  	scopes := []uint8{loadbalancer.ScopeExternal, loadbalancer.ScopeInternal}
   115  	for _, scope := range scopes {
   116  		// Validate all frontends as exact matches
   117  		// These should match only for external scope
   118  		exact_match_ok := scope == loadbalancer.ScopeExternal
   119  		addrCluster1 := cmtypes.MustParseAddrCluster("1.1.1.1")
   120  		addrCluster2 := cmtypes.MustParseAddrCluster("2.2.2.2")
   121  		frontend := loadbalancer.NewL3n4Addr(loadbalancer.TCP, addrCluster1, 10, scope)
   122  		require.Equal(t, exact_match_ok, frontends.LooseMatch(*frontend))
   123  		frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, addrCluster1, 20, scope)
   124  		require.Equal(t, exact_match_ok, frontends.LooseMatch(*frontend))
   125  		frontend = loadbalancer.NewL3n4Addr(loadbalancer.UDP, addrCluster2, 20, scope)
   126  		require.Equal(t, exact_match_ok, frontends.LooseMatch(*frontend))
   127  
   128  		// Validate protocol mismatch on exact match
   129  		frontend = loadbalancer.NewL3n4Addr(loadbalancer.TCP, addrCluster2, 20, scope)
   130  		require.Equal(t, false, frontends.LooseMatch(*frontend))
   131  
   132  		// Validate protocol wildcard matching
   133  		// These should match only for external scope
   134  		wild_match_ok := scope == loadbalancer.ScopeExternal
   135  		frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, addrCluster2, 20, scope)
   136  		require.Equal(t, wild_match_ok, frontends.LooseMatch(*frontend))
   137  		frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, addrCluster1, 10, scope)
   138  		require.Equal(t, wild_match_ok, frontends.LooseMatch(*frontend))
   139  		frontend = loadbalancer.NewL3n4Addr(loadbalancer.NONE, addrCluster1, 20, scope)
   140  		require.Equal(t, wild_match_ok, frontends.LooseMatch(*frontend))
   141  	}
   142  }
   143  
   144  func TestServiceCacheEndpoints(t *testing.T) {
   145  	endpoints := ParseEndpoints(&slim_corev1.Endpoints{
   146  		ObjectMeta: slim_metav1.ObjectMeta{
   147  			Name:      "foo",
   148  			Namespace: "bar",
   149  		},
   150  		Subsets: []slim_corev1.EndpointSubset{
   151  			{
   152  				Addresses: []slim_corev1.EndpointAddress{{IP: "2.2.2.2"}},
   153  				Ports: []slim_corev1.EndpointPort{
   154  					{
   155  						Name:     "http-test-svc",
   156  						Port:     8080,
   157  						Protocol: slim_corev1.ProtocolTCP,
   158  					},
   159  				},
   160  			},
   161  		},
   162  	})
   163  
   164  	updateEndpoints := func(svcCache *ServiceCache, swgEps *lock.StoppableWaitGroup) {
   165  		svcCache.UpdateEndpoints(endpoints, swgEps)
   166  	}
   167  	deleteEndpoints := func(svcCache *ServiceCache, swgEps *lock.StoppableWaitGroup) {
   168  		svcCache.DeleteEndpoints(endpoints.EndpointSliceID, swgEps)
   169  	}
   170  
   171  	testServiceCache(t, updateEndpoints, deleteEndpoints)
   172  }
   173  
   174  func TestServiceCacheEndpointSlice(t *testing.T) {
   175  	endpoints := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
   176  		AddressType: slim_discovery_v1.AddressTypeIPv4,
   177  		ObjectMeta: slim_metav1.ObjectMeta{
   178  			Name:      "foo-afbh9",
   179  			Namespace: "bar",
   180  			Labels: map[string]string{
   181  				slim_discovery_v1.LabelServiceName: "foo",
   182  			},
   183  		},
   184  		Endpoints: []slim_discovery_v1.Endpoint{
   185  			{
   186  				Addresses: []string{
   187  					"2.2.2.2",
   188  				},
   189  			},
   190  		},
   191  		Ports: []slim_discovery_v1.EndpointPort{
   192  			{
   193  				Name:     func() *string { a := "http-test-svc"; return &a }(),
   194  				Protocol: func() *slim_corev1.Protocol { a := slim_corev1.ProtocolTCP; return &a }(),
   195  				Port:     func() *int32 { a := int32(8080); return &a }(),
   196  			},
   197  		},
   198  	})
   199  
   200  	updateEndpoints := func(svcCache *ServiceCache, swgEps *lock.StoppableWaitGroup) {
   201  		svcCache.UpdateEndpoints(endpoints, swgEps)
   202  	}
   203  	deleteEndpoints := func(svcCache *ServiceCache, swgEps *lock.StoppableWaitGroup) {
   204  		svcCache.DeleteEndpoints(endpoints.EndpointSliceID, swgEps)
   205  	}
   206  
   207  	testServiceCache(t, updateEndpoints, deleteEndpoints)
   208  }
   209  
   210  func testServiceCache(t *testing.T,
   211  	updateEndpointsCB, deleteEndpointsCB func(svcCache *ServiceCache, swgEps *lock.StoppableWaitGroup)) {
   212  
   213  	db, nodeAddrs := newDB(t)
   214  	svcCache := NewServiceCache(db, nodeAddrs)
   215  
   216  	k8sSvc := &slim_corev1.Service{
   217  		ObjectMeta: slim_metav1.ObjectMeta{
   218  			Name:      "foo",
   219  			Namespace: "bar",
   220  			Labels: map[string]string{
   221  				"foo": "bar",
   222  			},
   223  		},
   224  		Spec: slim_corev1.ServiceSpec{
   225  			ClusterIP: "127.0.0.1",
   226  			Selector: map[string]string{
   227  				"foo": "bar",
   228  			},
   229  			Type: slim_corev1.ServiceTypeClusterIP,
   230  		},
   231  	}
   232  
   233  	swgSvcs := lock.NewStoppableWaitGroup()
   234  	svcID := svcCache.UpdateService(k8sSvc, swgSvcs)
   235  
   236  	time.Sleep(100 * time.Millisecond)
   237  
   238  	select {
   239  	case <-svcCache.Events:
   240  		t.Error("Unexpected service event received before endpoints have been imported")
   241  	default:
   242  	}
   243  
   244  	swgEps := lock.NewStoppableWaitGroup()
   245  	updateEndpointsCB(svcCache, swgEps)
   246  
   247  	// The service should be ready as both service and endpoints have been
   248  	// imported
   249  	require.Nil(t, testutils.WaitUntil(func() bool {
   250  		event := <-svcCache.Events
   251  		defer event.SWG.Done()
   252  		require.Equal(t, UpdateService, event.Action)
   253  		require.Equal(t, svcID, event.ID)
   254  		return true
   255  	}, 2*time.Second))
   256  
   257  	endpoints, ready := svcCache.correlateEndpoints(svcID)
   258  	require.Equal(t, true, ready)
   259  	require.Equal(t, "2.2.2.2:8080/TCP", endpoints.String())
   260  
   261  	// Updating the service without chaning it should not result in an event
   262  	svcCache.UpdateService(k8sSvc, swgSvcs)
   263  	time.Sleep(100 * time.Millisecond)
   264  	select {
   265  	case <-svcCache.Events:
   266  		t.Error("Unexpected service event received for unchanged service object")
   267  	default:
   268  	}
   269  
   270  	// Add late subscriber, it should receive all events until it unsubscribes
   271  	subCtx, subCancel := context.WithCancel(context.Background())
   272  	svcNotifications := stream.ToChannel(subCtx, svcCache.Notifications(), stream.WithBufferSize(1))
   273  
   274  	// Deleting the service will result in a service delete event
   275  	svcCache.DeleteService(k8sSvc, swgSvcs)
   276  	require.Nil(t, testutils.WaitUntil(func() bool {
   277  		event := <-svcCache.Events
   278  		defer event.SWG.Done()
   279  		require.Equal(t, DeleteService, event.Action)
   280  		require.Equal(t, svcID, event.ID)
   281  
   282  		n := <-svcNotifications
   283  		require.Equal(t, DeleteService, n.Action)
   284  		require.Equal(t, svcID, n.ID)
   285  
   286  		return true
   287  	}, 2*time.Second))
   288  
   289  	// Reinserting the service should re-match with the still existing endpoints
   290  	svcCache.UpdateService(k8sSvc, swgSvcs)
   291  	require.Nil(t, testutils.WaitUntil(func() bool {
   292  		event := <-svcCache.Events
   293  		defer event.SWG.Done()
   294  		require.Equal(t, UpdateService, event.Action)
   295  		require.Equal(t, svcID, event.ID)
   296  
   297  		n := <-svcNotifications
   298  		require.Equal(t, UpdateService, n.Action)
   299  		require.Equal(t, svcID, n.ID)
   300  
   301  		return true
   302  	}, 2*time.Second))
   303  
   304  	// Deleting the endpoints will result in a service update event
   305  	deleteEndpointsCB(svcCache, swgEps)
   306  	require.Nil(t, testutils.WaitUntil(func() bool {
   307  		event := <-svcCache.Events
   308  		defer event.SWG.Done()
   309  		require.Equal(t, UpdateService, event.Action)
   310  		require.Equal(t, svcID, event.ID)
   311  
   312  		n := <-svcNotifications
   313  		require.Equal(t, UpdateService, n.Action)
   314  		require.Equal(t, svcID, n.ID)
   315  
   316  		return true
   317  	}, 2*time.Second))
   318  
   319  	// Stop subscription and wait for it to expire
   320  	subCancel()
   321  	require.Nil(t, testutils.WaitUntil(func() bool {
   322  		_, stillSubscribed := <-svcNotifications
   323  		return !stillSubscribed
   324  	}, 2*time.Second))
   325  
   326  	endpoints, serviceReady := svcCache.correlateEndpoints(svcID)
   327  	require.Equal(t, false, serviceReady)
   328  	require.Equal(t, "", endpoints.String())
   329  
   330  	// Reinserting the endpoints should re-match with the still existing service
   331  	updateEndpointsCB(svcCache, swgEps)
   332  	require.Nil(t, testutils.WaitUntil(func() bool {
   333  		event := <-svcCache.Events
   334  		defer event.SWG.Done()
   335  		require.Equal(t, UpdateService, event.Action)
   336  		require.Equal(t, svcID, event.ID)
   337  		return true
   338  	}, 2*time.Second))
   339  
   340  	endpoints, serviceReady = svcCache.correlateEndpoints(svcID)
   341  	require.Equal(t, true, serviceReady)
   342  	require.Equal(t, "2.2.2.2:8080/TCP", endpoints.String())
   343  
   344  	// Deleting the service will result in a service delete event
   345  	svcCache.DeleteService(k8sSvc, swgSvcs)
   346  	require.Nil(t, testutils.WaitUntil(func() bool {
   347  		event := <-svcCache.Events
   348  		defer event.SWG.Done()
   349  		require.Equal(t, DeleteService, event.Action)
   350  		require.Equal(t, svcID, event.ID)
   351  		return true
   352  	}, 2*time.Second))
   353  
   354  	// Deleting the endpoints will not emit an event as the notification
   355  	// was sent out when the service was deleted.
   356  	deleteEndpointsCB(svcCache, swgEps)
   357  	time.Sleep(100 * time.Millisecond)
   358  	select {
   359  	case <-svcCache.Events:
   360  		t.Error("Unexpected service delete event received")
   361  	default:
   362  	}
   363  
   364  	swgSvcs.Stop()
   365  	require.Nil(t, testutils.WaitUntil(func() bool {
   366  		swgSvcs.Wait()
   367  		return true
   368  	}, 2*time.Second))
   369  
   370  	swgEps.Stop()
   371  	require.Nil(t, testutils.WaitUntil(func() bool {
   372  		swgEps.Wait()
   373  		return true
   374  	}, 2*time.Second))
   375  }
   376  
   377  func TestForEachService(t *testing.T) {
   378  	db, nodeAddrs := newDB(t)
   379  	svcCache := NewServiceCache(db, nodeAddrs)
   380  
   381  	k8sSvc1 := &slim_corev1.Service{
   382  		ObjectMeta: slim_metav1.ObjectMeta{
   383  			Name:      "foo",
   384  			Namespace: "bar",
   385  			Labels: map[string]string{
   386  				"foo": "bar",
   387  			},
   388  		},
   389  		Spec: slim_corev1.ServiceSpec{
   390  			ClusterIP: "127.0.0.1",
   391  			Selector: map[string]string{
   392  				"foo": "bar",
   393  			},
   394  			Type: slim_corev1.ServiceTypeClusterIP,
   395  		},
   396  	}
   397  	k8sEndpoints1 := ParseEndpoints(&slim_corev1.Endpoints{
   398  		ObjectMeta: slim_metav1.ObjectMeta{
   399  			Name:      "foo",
   400  			Namespace: "bar",
   401  		},
   402  		Subsets: []slim_corev1.EndpointSubset{
   403  			{
   404  				Addresses: []slim_corev1.EndpointAddress{{IP: "2.2.2.2"}},
   405  				Ports: []slim_corev1.EndpointPort{
   406  					{
   407  						Name:     "http-test-svc",
   408  						Port:     8080,
   409  						Protocol: slim_corev1.ProtocolTCP,
   410  					},
   411  				},
   412  			},
   413  		},
   414  	})
   415  
   416  	k8sSvc2 := &slim_corev1.Service{
   417  		ObjectMeta: slim_metav1.ObjectMeta{
   418  			Name:      "baz",
   419  			Namespace: "qux",
   420  		},
   421  		Spec: slim_corev1.ServiceSpec{
   422  			ClusterIP: "192.168.1.1",
   423  			Type:      slim_corev1.ServiceTypeClusterIP,
   424  		},
   425  	}
   426  	k8sEndpoints2 := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
   427  		AddressType: slim_discovery_v1.AddressTypeIPv4,
   428  		ObjectMeta: slim_metav1.ObjectMeta{
   429  			Name:      "baz-xxxxx",
   430  			Namespace: "qux",
   431  			Labels: map[string]string{
   432  				slim_discovery_v1.LabelServiceName: "baz",
   433  			},
   434  		},
   435  		Endpoints: []slim_discovery_v1.Endpoint{
   436  			{
   437  				Addresses: []string{
   438  					"1.1.1.1",
   439  					"1.0.0.1",
   440  				},
   441  			},
   442  		},
   443  	})
   444  
   445  	swg := lock.NewStoppableWaitGroup()
   446  	svcCache.UpdateService(k8sSvc1, swg)
   447  	svcCache.UpdateService(k8sSvc2, swg)
   448  
   449  	svcID1, eps1 := svcCache.UpdateEndpoints(k8sEndpoints1, swg)
   450  	require.Nil(t, testutils.WaitUntil(func() bool {
   451  		event := <-svcCache.Events
   452  		defer event.SWG.Done()
   453  		require.Equal(t, UpdateService, event.Action)
   454  		require.Equal(t, svcID1, event.ID)
   455  		require.Equal(t, eps1, event.Endpoints)
   456  		return true
   457  	}, 2*time.Second))
   458  
   459  	svcID2, eps2 := svcCache.UpdateEndpoints(k8sEndpoints2, swg)
   460  	require.Nil(t, testutils.WaitUntil(func() bool {
   461  		println("waiting for events2")
   462  		event := <-svcCache.Events
   463  		defer event.SWG.Done()
   464  		require.Equal(t, UpdateService, event.Action)
   465  		require.Equal(t, svcID2, event.ID)
   466  		require.Equal(t, eps2, event.Endpoints)
   467  		return true
   468  	}, 2*time.Second))
   469  
   470  	services := map[ServiceID]*Endpoints{}
   471  	svcCache.ForEachService(func(svcID ServiceID, svc *Service, eps *Endpoints) bool {
   472  		services[svcID] = eps
   473  		return true
   474  	})
   475  	require.Equal(t, map[ServiceID]*Endpoints{
   476  		svcID1: eps1,
   477  		svcID2: eps2,
   478  	}, services)
   479  }
   480  
   481  func TestCacheActionString(t *testing.T) {
   482  	require.Equal(t, "service-updated", UpdateService.String())
   483  	require.Equal(t, "service-deleted", DeleteService.String())
   484  }
   485  
   486  func TestServiceMutators(t *testing.T) {
   487  	var m1, m2 int
   488  
   489  	db, nodeAddrs := newDB(t)
   490  	svcCache := NewServiceCache(db, nodeAddrs)
   491  
   492  	svcCache.ServiceMutators = append(svcCache.ServiceMutators,
   493  		func(svc *slim_corev1.Service, svcInfo *Service) { m1++ },
   494  		func(svc *slim_corev1.Service, svcInfo *Service) { m2++ },
   495  	)
   496  	swg := lock.NewStoppableWaitGroup()
   497  	svcCache.UpdateService(&slim_corev1.Service{
   498  		ObjectMeta: slim_metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
   499  		Spec: slim_corev1.ServiceSpec{
   500  			ClusterIP: "127.0.0.1",
   501  			Selector:  map[string]string{"foo": "bar"},
   502  			Type:      slim_corev1.ServiceTypeClusterIP,
   503  		},
   504  	}, swg)
   505  
   506  	// Assert that the service mutators configured have been executed.
   507  	require.Equal(t, 1, m1)
   508  	require.Equal(t, 1, m2)
   509  }
   510  
   511  func TestExternalServiceMerging(t *testing.T) {
   512  	db, nodeAddrs := newDB(t)
   513  	svcCache := NewServiceCache(db, nodeAddrs)
   514  
   515  	k8sSvc := &slim_corev1.Service{
   516  		ObjectMeta: slim_metav1.ObjectMeta{
   517  			Name:      "foo",
   518  			Namespace: "bar",
   519  			Annotations: map[string]string{
   520  				"service.cilium.io/global": "true",
   521  			},
   522  		},
   523  		Spec: slim_corev1.ServiceSpec{
   524  			ClusterIP: "127.0.0.1",
   525  			Type:      slim_corev1.ServiceTypeClusterIP,
   526  			Ports: []slim_corev1.ServicePort{
   527  				{
   528  					Name:     "foo",
   529  					Protocol: slim_corev1.ProtocolTCP,
   530  					Port:     80,
   531  				},
   532  			},
   533  		},
   534  	}
   535  
   536  	swgSvcs := lock.NewStoppableWaitGroup()
   537  	svcID := svcCache.UpdateService(k8sSvc, swgSvcs)
   538  
   539  	endpoints := ParseEndpoints(&slim_corev1.Endpoints{
   540  		ObjectMeta: slim_metav1.ObjectMeta{
   541  			Name:      "foo",
   542  			Namespace: "bar",
   543  		},
   544  		Subsets: []slim_corev1.EndpointSubset{
   545  			{
   546  				Addresses: []slim_corev1.EndpointAddress{{IP: "2.2.2.2"}},
   547  				Ports: []slim_corev1.EndpointPort{
   548  					{
   549  						Name:     "http-test-svc",
   550  						Port:     8080,
   551  						Protocol: slim_corev1.ProtocolTCP,
   552  					},
   553  				},
   554  			},
   555  		},
   556  	})
   557  
   558  	swgEps := lock.NewStoppableWaitGroup()
   559  	svcCache.UpdateEndpoints(endpoints, swgEps)
   560  
   561  	// The service should be ready as both service and endpoints have been
   562  	// imported
   563  	require.Nil(t, testutils.WaitUntil(func() bool {
   564  		event := <-svcCache.Events
   565  		defer event.SWG.Done()
   566  		require.Equal(t, UpdateService, event.Action)
   567  		require.Equal(t, svcID, event.ID)
   568  		return true
   569  	}, 2*time.Second))
   570  
   571  	// Merging a service update with own cluster name must not result in update
   572  	svcCache.MergeExternalServiceUpdate(&serviceStore.ClusterService{
   573  		Cluster:   option.Config.ClusterName,
   574  		Namespace: "bar",
   575  		Name:      "foo",
   576  		Frontends: map[string]serviceStore.PortConfiguration{
   577  			"1.1.1.1": {},
   578  		},
   579  		Backends: map[string]serviceStore.PortConfiguration{
   580  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   581  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   582  			},
   583  		},
   584  	},
   585  		swgSvcs,
   586  	)
   587  
   588  	time.Sleep(100 * time.Millisecond)
   589  
   590  	select {
   591  	case <-svcCache.Events:
   592  		t.Error("Unexpected service event received")
   593  	default:
   594  	}
   595  
   596  	svcCache.MergeExternalServiceUpdate(&serviceStore.ClusterService{
   597  		Cluster:   "cluster1",
   598  		Namespace: "bar",
   599  		Name:      "foo",
   600  		Frontends: map[string]serviceStore.PortConfiguration{
   601  			"1.1.1.1": {},
   602  		},
   603  		Backends: map[string]serviceStore.PortConfiguration{
   604  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   605  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   606  			},
   607  		},
   608  		IncludeExternal: false,
   609  		Shared:          false,
   610  	},
   611  		swgSvcs,
   612  	)
   613  
   614  	// Adding non-shared remote endpoints will not trigger a service update, regardless of whether
   615  	// IncludeExternal is set (i.e., the service is marked as a global one in the remote cluster).
   616  	require.Nil(t, testutils.WaitUntil(func() bool {
   617  		event := <-svcCache.Events
   618  		defer event.SWG.Done()
   619  		require.Equal(t, UpdateService, event.Action)
   620  		require.Equal(t, svcID, event.ID)
   621  
   622  		require.Equal(t, 1, len(event.Endpoints.Backends))
   623  		require.EqualValues(t, &Backend{
   624  			Ports: serviceStore.PortConfiguration{
   625  				"http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080},
   626  			},
   627  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("2.2.2.2")])
   628  
   629  		return true
   630  	}, 2*time.Second))
   631  
   632  	svcCache.MergeExternalServiceUpdate(&serviceStore.ClusterService{
   633  		Cluster:   "cluster1",
   634  		Namespace: "bar",
   635  		Name:      "foo",
   636  		Frontends: map[string]serviceStore.PortConfiguration{
   637  			"1.1.1.1": {},
   638  		},
   639  		Backends: map[string]serviceStore.PortConfiguration{
   640  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   641  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   642  			},
   643  		},
   644  		IncludeExternal: true,
   645  		Shared:          false,
   646  	},
   647  		swgSvcs,
   648  	)
   649  
   650  	// Adding non-shared remote endpoints will not trigger a service update, regardless of whether
   651  	// IncludeExternal is set (i.e., the service is marked as a global one in the remote cluster).
   652  	require.Nil(t, testutils.WaitUntil(func() bool {
   653  		event := <-svcCache.Events
   654  		defer event.SWG.Done()
   655  		require.Equal(t, UpdateService, event.Action)
   656  		require.Equal(t, svcID, event.ID)
   657  
   658  		require.Equal(t, 1, len(event.Endpoints.Backends))
   659  		require.EqualValues(t, &Backend{
   660  			Ports: serviceStore.PortConfiguration{
   661  				"http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080},
   662  			},
   663  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("2.2.2.2")])
   664  
   665  		return true
   666  	}, 2*time.Second))
   667  
   668  	// We do not test the case with shared remote endpoints and IncludeExternal not set
   669  	// (i.e., the service is not marked as a global one in the remote cluster).
   670  	// Indeed, this condition shall never happen, since a shared service shall always be global.
   671  
   672  	svcCache.MergeExternalServiceUpdate(&serviceStore.ClusterService{
   673  		Cluster:   "cluster1",
   674  		Namespace: "bar",
   675  		Name:      "foo",
   676  		Frontends: map[string]serviceStore.PortConfiguration{
   677  			"1.1.1.1": {},
   678  		},
   679  		Backends: map[string]serviceStore.PortConfiguration{
   680  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   681  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   682  			},
   683  		},
   684  		IncludeExternal: true,
   685  		Shared:          true,
   686  	},
   687  		swgSvcs,
   688  	)
   689  
   690  	// Adding shared remote endpoints will trigger a service update, in case IncludeExternal
   691  	// is set (i.e., the service is marked as a global one in the remote cluster).
   692  	require.Nil(t, testutils.WaitUntil(func() bool {
   693  		event := <-svcCache.Events
   694  		defer event.SWG.Done()
   695  		require.Equal(t, UpdateService, event.Action)
   696  		require.Equal(t, svcID, event.ID)
   697  		require.EqualValues(t, &Backend{
   698  			Ports: serviceStore.PortConfiguration{
   699  				"http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080},
   700  			},
   701  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("2.2.2.2")])
   702  
   703  		require.EqualValues(t, &Backend{
   704  			Ports: serviceStore.PortConfiguration{
   705  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   706  			},
   707  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("3.3.3.3")])
   708  
   709  		return true
   710  	}, 2*time.Second))
   711  
   712  	// Merging a service for another name should not trigger any updates
   713  	svcCache.MergeExternalServiceUpdate(&serviceStore.ClusterService{
   714  		Cluster:   "cluster",
   715  		Namespace: "bar",
   716  		Name:      "foo2",
   717  		Frontends: map[string]serviceStore.PortConfiguration{
   718  			"1.1.1.1": {},
   719  		},
   720  		Backends: map[string]serviceStore.PortConfiguration{
   721  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   722  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   723  			},
   724  		},
   725  		IncludeExternal: true,
   726  		Shared:          true,
   727  	},
   728  		swgSvcs,
   729  	)
   730  
   731  	time.Sleep(100 * time.Millisecond)
   732  
   733  	select {
   734  	case <-svcCache.Events:
   735  		t.Error("Unexpected service event received")
   736  	default:
   737  	}
   738  
   739  	// Adding the service later must trigger an update
   740  	svcID2 := svcCache.UpdateService(
   741  		&slim_corev1.Service{
   742  			ObjectMeta: slim_metav1.ObjectMeta{
   743  				Name:      "foo2",
   744  				Namespace: "bar",
   745  				Labels: map[string]string{
   746  					"foo": "bar",
   747  				},
   748  				Annotations: map[string]string{
   749  					"service.cilium.io/global": "true",
   750  				},
   751  			},
   752  			Spec: slim_corev1.ServiceSpec{
   753  				ClusterIP: "127.0.0.2",
   754  				Selector: map[string]string{
   755  					"foo": "bar",
   756  				},
   757  				Type: slim_corev1.ServiceTypeClusterIP,
   758  			},
   759  		},
   760  		swgSvcs,
   761  	)
   762  
   763  	require.Nil(t, testutils.WaitUntil(func() bool {
   764  		event := <-svcCache.Events
   765  		defer event.SWG.Done()
   766  		require.Equal(t, UpdateService, event.Action)
   767  		require.Equal(t, svcID2, event.ID)
   768  		return true
   769  	}, 2*time.Second))
   770  
   771  	cluster2svc := &serviceStore.ClusterService{
   772  		Cluster:   "cluster2",
   773  		Namespace: "bar",
   774  		Name:      "foo",
   775  		Frontends: map[string]serviceStore.PortConfiguration{
   776  			"1.1.1.1": {},
   777  		},
   778  		Backends: map[string]serviceStore.PortConfiguration{
   779  			"4.4.4.4": map[string]*loadbalancer.L4Addr{
   780  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   781  			},
   782  		},
   783  		IncludeExternal: true,
   784  		Shared:          true,
   785  	}
   786  
   787  	// Adding another cluster to the first service will trigger an event
   788  	svcCache.MergeExternalServiceUpdate(cluster2svc, swgSvcs)
   789  	require.Nil(t, testutils.WaitUntil(func() bool {
   790  		event := <-svcCache.Events
   791  		defer event.SWG.Done()
   792  		require.Equal(t, UpdateService, event.Action)
   793  		require.EqualValues(t, &Backend{
   794  			Ports: serviceStore.PortConfiguration{
   795  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   796  			},
   797  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("4.4.4.4")])
   798  
   799  		return true
   800  	}, 2*time.Second))
   801  
   802  	svcCache.MergeExternalServiceDelete(cluster2svc, swgSvcs)
   803  	require.Nil(t, testutils.WaitUntil(func() bool {
   804  		event := <-svcCache.Events
   805  		defer event.SWG.Done()
   806  		require.Equal(t, UpdateService, event.Action)
   807  		require.Nil(t, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("4.4.4.4")])
   808  		return true
   809  	}, 2*time.Second))
   810  
   811  	// Deletion of the service frontend will trigger the delete notification
   812  	svcCache.DeleteService(k8sSvc, swgSvcs)
   813  	require.Nil(t, testutils.WaitUntil(func() bool {
   814  		event := <-svcCache.Events
   815  		defer event.SWG.Done()
   816  		require.Equal(t, DeleteService, event.Action)
   817  		require.Equal(t, svcID, event.ID)
   818  		return true
   819  	}, 2*time.Second))
   820  
   821  	// When re-adding the service, the remote endpoints of cluster1 must still be present
   822  	svcCache.UpdateService(k8sSvc, swgSvcs)
   823  	require.Nil(t, testutils.WaitUntil(func() bool {
   824  		event := <-svcCache.Events
   825  		defer event.SWG.Done()
   826  		require.Equal(t, UpdateService, event.Action)
   827  		require.Equal(t, svcID, event.ID)
   828  		require.EqualValues(t, &Backend{
   829  			Ports: serviceStore.PortConfiguration{
   830  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   831  			},
   832  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("3.3.3.3")])
   833  		return true
   834  	}, 2*time.Second))
   835  
   836  	k8sSvcID, _ := ParseService(k8sSvc, nil)
   837  	addresses := svcCache.GetServiceIP(k8sSvcID)
   838  	require.EqualValues(t, loadbalancer.NewL3n4Addr(loadbalancer.TCP, cmtypes.MustParseAddrCluster("127.0.0.1"), 80, loadbalancer.ScopeExternal), addresses)
   839  
   840  	swgSvcs.Stop()
   841  	require.Nil(t, testutils.WaitUntil(func() bool {
   842  		swgSvcs.Wait()
   843  		return true
   844  	}, 2*time.Second))
   845  
   846  	swgEps.Stop()
   847  	require.Nil(t, testutils.WaitUntil(func() bool {
   848  		swgEps.Wait()
   849  		return true
   850  	}, 2*time.Second))
   851  }
   852  
   853  func TestExternalServiceDeletion(t *testing.T) {
   854  	const cluster = "cluster"
   855  
   856  	createEndpoints := func(clusters ...string) externalEndpoints {
   857  		eeps := newExternalEndpoints()
   858  		for i, cluster := range clusters {
   859  			eps := newEndpoints()
   860  			eps.Backends[cmtypes.MustParseAddrCluster(fmt.Sprintf("1.1.1.%d", i))] = &Backend{}
   861  			eeps.endpoints[cluster] = eps
   862  		}
   863  
   864  		return eeps
   865  	}
   866  
   867  	svc := Service{IncludeExternal: true, Shared: true}
   868  	clsvc := serviceStore.ClusterService{Cluster: cluster, Namespace: "bar", Name: "foo"}
   869  	id1 := ServiceID{Namespace: "bar", Name: "foo"}
   870  	id2 := ServiceID{Cluster: cluster, Namespace: "bar", Name: "foo"}
   871  
   872  	swg := lock.NewStoppableWaitGroup()
   873  	db, nodeAddrs := newDB(t)
   874  	svcCache := NewServiceCache(db, nodeAddrs)
   875  
   876  	// Store the service with the non-cluster-aware ID
   877  	svcCache.services[id1] = &svc
   878  	svcCache.externalEndpoints[id1] = createEndpoints(cluster)
   879  
   880  	svcCache.MergeExternalServiceDelete(&clsvc, swg)
   881  	_, ok := svcCache.services[id1]
   882  	require.Equal(t, false, ok)
   883  	_, ok = svcCache.externalEndpoints[id1]
   884  	require.Equal(t, false, ok)
   885  
   886  	require.Nil(t, testutils.WaitUntil(func() bool {
   887  		event := <-svcCache.Events
   888  		defer event.SWG.Done()
   889  		require.Equal(t, DeleteService, event.Action)
   890  		require.Equal(t, id1, event.ID)
   891  		return true
   892  	}, 2*time.Second))
   893  
   894  	// Store the service with the non-cluster-aware ID and multiple endpoints
   895  	svcCache.services[id1] = &svc
   896  	svcCache.externalEndpoints[id1] = createEndpoints(cluster, "other")
   897  
   898  	svcCache.MergeExternalServiceDelete(&clsvc, swg)
   899  	_, ok = svcCache.services[id1]
   900  	require.Equal(t, true, ok)
   901  	_, ok = svcCache.externalEndpoints[id1]
   902  	require.Equal(t, true, ok)
   903  	_, ok = svcCache.externalEndpoints[id1].endpoints[cluster]
   904  	require.Equal(t, false, ok)
   905  
   906  	require.Nil(t, testutils.WaitUntil(func() bool {
   907  		event := <-svcCache.Events
   908  		defer event.SWG.Done()
   909  		require.Equal(t, UpdateService, event.Action)
   910  		require.Equal(t, id1, event.ID)
   911  		return true
   912  	}, 2*time.Second))
   913  
   914  	// Store the service with the cluster-aware ID
   915  	svcCache.services[id2] = &svc
   916  	svcCache.externalEndpoints[id2] = createEndpoints(cluster)
   917  
   918  	svcCache.MergeExternalServiceDelete(&clsvc, swg)
   919  	_, ok = svcCache.services[id2]
   920  	require.Equal(t, false, ok)
   921  	_, ok = svcCache.externalEndpoints[id2]
   922  	require.Equal(t, false, ok)
   923  
   924  	require.Nil(t, testutils.WaitUntil(func() bool {
   925  		event := <-svcCache.Events
   926  		defer event.SWG.Done()
   927  		require.Equal(t, DeleteService, event.Action)
   928  		require.Equal(t, id2, event.ID)
   929  		return true
   930  	}, 2*time.Second))
   931  }
   932  
   933  func TestClusterServiceMerging(t *testing.T) {
   934  	db, nodeAddrs := newDB(t)
   935  	svcCache := NewServiceCache(db, nodeAddrs)
   936  	swgSvcs := lock.NewStoppableWaitGroup()
   937  	swgEps := lock.NewStoppableWaitGroup()
   938  
   939  	svcID := ServiceID{Name: "foo", Namespace: "bar"}
   940  
   941  	endpoints := ParseEndpoints(&slim_corev1.Endpoints{
   942  		ObjectMeta: slim_metav1.ObjectMeta{
   943  			Namespace: svcID.Namespace,
   944  			Name:      svcID.Name,
   945  		},
   946  		Subsets: []slim_corev1.EndpointSubset{
   947  			{
   948  				Addresses: []slim_corev1.EndpointAddress{{IP: "2.2.2.2"}},
   949  				Ports: []slim_corev1.EndpointPort{
   950  					{
   951  						Name:     "http-test-svc",
   952  						Port:     8080,
   953  						Protocol: slim_corev1.ProtocolTCP,
   954  					},
   955  				},
   956  			},
   957  		},
   958  	})
   959  
   960  	svcCache.UpdateEndpoints(endpoints, swgEps)
   961  
   962  	svcCache.MergeClusterServiceUpdate(&serviceStore.ClusterService{
   963  		Cluster:   option.Config.ClusterName,
   964  		Namespace: svcID.Namespace,
   965  		Name:      svcID.Name,
   966  		Frontends: map[string]serviceStore.PortConfiguration{
   967  			"1.1.1.1": {},
   968  		},
   969  		Backends: map[string]serviceStore.PortConfiguration{
   970  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
   971  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   972  			},
   973  		},
   974  		IncludeExternal: false,
   975  		Shared:          false,
   976  	}, swgSvcs)
   977  
   978  	// Adding a service will trigger the corresponding update containing all ready backends,
   979  	// regardless of whether it is marked as global or shared (since the cluster name matches).
   980  	require.Nil(t, testutils.WaitUntil(func() bool {
   981  		event := <-svcCache.Events
   982  		defer event.SWG.Done()
   983  		require.Equal(t, UpdateService, event.Action)
   984  		require.Equal(t, svcID, event.ID)
   985  		require.EqualValues(t, &Backend{
   986  			Ports: serviceStore.PortConfiguration{
   987  				"http-test-svc": {Protocol: loadbalancer.TCP, Port: 8080},
   988  			},
   989  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("2.2.2.2")])
   990  
   991  		require.EqualValues(t, &Backend{
   992  			Ports: serviceStore.PortConfiguration{
   993  				"port": {Protocol: loadbalancer.TCP, Port: 80},
   994  			},
   995  		}, event.Endpoints.Backends[cmtypes.MustParseAddrCluster("3.3.3.3")])
   996  
   997  		return true
   998  	}, 2*time.Second))
   999  }
  1000  
  1001  func TestNonSharedService(t *testing.T) {
  1002  	db, nodeAddrs := newDB(t)
  1003  	svcCache := NewServiceCache(db, nodeAddrs)
  1004  
  1005  	k8sSvc := &slim_corev1.Service{
  1006  		ObjectMeta: slim_metav1.ObjectMeta{
  1007  			Name:      "foo",
  1008  			Namespace: "bar",
  1009  			Annotations: map[string]string{
  1010  				"service.cilium.io/global": "false",
  1011  			},
  1012  		},
  1013  		Spec: slim_corev1.ServiceSpec{
  1014  			ClusterIP: "127.0.0.1",
  1015  			Type:      slim_corev1.ServiceTypeClusterIP,
  1016  		},
  1017  	}
  1018  
  1019  	swgSvcs := lock.NewStoppableWaitGroup()
  1020  	svcCache.UpdateService(k8sSvc, swgSvcs)
  1021  
  1022  	svcCache.MergeExternalServiceUpdate(&serviceStore.ClusterService{
  1023  		Cluster:   "cluster1",
  1024  		Namespace: "bar",
  1025  		Name:      "foo",
  1026  		Backends: map[string]serviceStore.PortConfiguration{
  1027  			"3.3.3.3": map[string]*loadbalancer.L4Addr{
  1028  				"port": {Protocol: loadbalancer.TCP, Port: 80},
  1029  			},
  1030  		},
  1031  	},
  1032  		swgSvcs,
  1033  	)
  1034  
  1035  	// The service is unshared, it should not trigger an update
  1036  	time.Sleep(100 * time.Millisecond)
  1037  
  1038  	select {
  1039  	case <-svcCache.Events:
  1040  		t.Error("Unexpected service event received")
  1041  	default:
  1042  	}
  1043  
  1044  	swgSvcs.Stop()
  1045  	require.Nil(t, testutils.WaitUntil(func() bool {
  1046  		swgSvcs.Wait()
  1047  		return true
  1048  	}, 2*time.Second))
  1049  }
  1050  
  1051  func TestServiceCacheWith2EndpointSlice(t *testing.T) {
  1052  	k8sEndpointSlice1 := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
  1053  		AddressType: slim_discovery_v1.AddressTypeIPv4,
  1054  		ObjectMeta: slim_metav1.ObjectMeta{
  1055  			Name:      "foo-yyyyy",
  1056  			Namespace: "bar",
  1057  			Labels: map[string]string{
  1058  				slim_discovery_v1.LabelServiceName: "foo",
  1059  			},
  1060  		},
  1061  		Endpoints: []slim_discovery_v1.Endpoint{
  1062  			{
  1063  				Addresses: []string{
  1064  					"2.2.2.2",
  1065  				},
  1066  			},
  1067  		},
  1068  		Ports: []slim_discovery_v1.EndpointPort{
  1069  			{
  1070  				Name:     func() *string { a := "http-test-svc"; return &a }(),
  1071  				Protocol: func() *slim_corev1.Protocol { a := slim_corev1.ProtocolTCP; return &a }(),
  1072  				Port:     func() *int32 { a := int32(8080); return &a }(),
  1073  			},
  1074  		},
  1075  	})
  1076  
  1077  	k8sEndpointSlice2 := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
  1078  		AddressType: slim_discovery_v1.AddressTypeIPv4,
  1079  		ObjectMeta: slim_metav1.ObjectMeta{
  1080  			Name:      "foo-xxxxx",
  1081  			Namespace: "bar",
  1082  			Labels: map[string]string{
  1083  				slim_discovery_v1.LabelServiceName: "foo",
  1084  			},
  1085  		},
  1086  		Endpoints: []slim_discovery_v1.Endpoint{
  1087  			{
  1088  				Addresses: []string{
  1089  					"2.2.2.3",
  1090  				},
  1091  			},
  1092  		},
  1093  		Ports: []slim_discovery_v1.EndpointPort{
  1094  			{
  1095  				Name:     func() *string { a := "http-test-svc"; return &a }(),
  1096  				Protocol: func() *slim_corev1.Protocol { a := slim_corev1.ProtocolTCP; return &a }(),
  1097  				Port:     func() *int32 { a := int32(8080); return &a }(),
  1098  			},
  1099  		},
  1100  	})
  1101  
  1102  	k8sEndpointSlice3 := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
  1103  		AddressType: slim_discovery_v1.AddressTypeIPv4,
  1104  		ObjectMeta: slim_metav1.ObjectMeta{
  1105  			Name:      "foo-xxxxx",
  1106  			Namespace: "baz",
  1107  			Labels: map[string]string{
  1108  				slim_discovery_v1.LabelServiceName: "foo",
  1109  			},
  1110  		},
  1111  		Endpoints: []slim_discovery_v1.Endpoint{
  1112  			{
  1113  				Addresses: []string{
  1114  					"2.2.2.4",
  1115  				},
  1116  			},
  1117  		},
  1118  		Ports: []slim_discovery_v1.EndpointPort{
  1119  			{
  1120  				Name:     func() *string { a := "http-test-svc"; return &a }(),
  1121  				Protocol: func() *slim_corev1.Protocol { a := slim_corev1.ProtocolTCP; return &a }(),
  1122  				Port:     func() *int32 { a := int32(8080); return &a }(),
  1123  			},
  1124  		},
  1125  	})
  1126  
  1127  	db, nodeAddrs := newDB(t)
  1128  	svcCache := NewServiceCache(db, nodeAddrs)
  1129  
  1130  	k8sSvc := &slim_corev1.Service{
  1131  		ObjectMeta: slim_metav1.ObjectMeta{
  1132  			Name:      "foo",
  1133  			Namespace: "bar",
  1134  			Labels: map[string]string{
  1135  				"foo": "bar",
  1136  			},
  1137  		},
  1138  		Spec: slim_corev1.ServiceSpec{
  1139  			ClusterIP: "127.0.0.1",
  1140  			Selector: map[string]string{
  1141  				"foo": "bar",
  1142  			},
  1143  			Type: slim_corev1.ServiceTypeClusterIP,
  1144  		},
  1145  	}
  1146  
  1147  	swgSvcs := lock.NewStoppableWaitGroup()
  1148  	svcID := svcCache.UpdateService(k8sSvc, swgSvcs)
  1149  
  1150  	time.Sleep(100 * time.Millisecond)
  1151  
  1152  	select {
  1153  	case <-svcCache.Events:
  1154  		t.Error("Unexpected service event received before endpoints have been imported")
  1155  	default:
  1156  	}
  1157  
  1158  	swgEps := lock.NewStoppableWaitGroup()
  1159  	svcCache.UpdateEndpoints(k8sEndpointSlice1, swgEps)
  1160  	svcCache.UpdateEndpoints(k8sEndpointSlice2, swgEps)
  1161  	svcCache.UpdateEndpoints(k8sEndpointSlice3, swgEps)
  1162  
  1163  	// The service should be ready as both service and endpoints have been
  1164  	// imported for k8sEndpointSlice1
  1165  	require.Nil(t, testutils.WaitUntil(func() bool {
  1166  		event := <-svcCache.Events
  1167  		defer event.SWG.Done()
  1168  		require.Equal(t, UpdateService, event.Action)
  1169  		require.Equal(t, svcID, event.ID)
  1170  		return true
  1171  	}, 2*time.Second))
  1172  
  1173  	// The service should be ready as both service and endpoints have been
  1174  	// imported for k8sEndpointSlice2
  1175  	require.Nil(t, testutils.WaitUntil(func() bool {
  1176  		event := <-svcCache.Events
  1177  		defer event.SWG.Done()
  1178  		require.Equal(t, UpdateService, event.Action)
  1179  		require.Equal(t, svcID, event.ID)
  1180  		return true
  1181  	}, 2*time.Second))
  1182  
  1183  	select {
  1184  	case <-svcCache.Events:
  1185  		t.Error("Unexpected service event received when endpoints not selected by a service have been imported")
  1186  	default:
  1187  	}
  1188  	endpoints, ready := svcCache.correlateEndpoints(svcID)
  1189  	require.Equal(t, true, ready)
  1190  	require.Equal(t, "2.2.2.2:8080/TCP,2.2.2.3:8080/TCP", endpoints.String())
  1191  
  1192  	// Updating the service without changing it should not result in an event
  1193  	svcCache.UpdateService(k8sSvc, swgSvcs)
  1194  	time.Sleep(100 * time.Millisecond)
  1195  	select {
  1196  	case <-svcCache.Events:
  1197  		t.Error("Unexpected service event received for unchanged service object")
  1198  	default:
  1199  	}
  1200  
  1201  	// Deleting the service will result in a service delete event
  1202  	svcCache.DeleteService(k8sSvc, swgSvcs)
  1203  	require.Nil(t, testutils.WaitUntil(func() bool {
  1204  		event := <-svcCache.Events
  1205  		defer event.SWG.Done()
  1206  		require.Equal(t, DeleteService, event.Action)
  1207  		require.Equal(t, svcID, event.ID)
  1208  		return true
  1209  	}, 2*time.Second))
  1210  
  1211  	// Reinserting the service should re-match with the still existing endpoints
  1212  	svcCache.UpdateService(k8sSvc, swgSvcs)
  1213  	require.Nil(t, testutils.WaitUntil(func() bool {
  1214  		event := <-svcCache.Events
  1215  		defer event.SWG.Done()
  1216  		require.Equal(t, UpdateService, event.Action)
  1217  		require.Equal(t, svcID, event.ID)
  1218  		return true
  1219  	}, 2*time.Second))
  1220  
  1221  	// Deleting the k8sEndpointSlice2 will result in a service update event
  1222  	svcCache.DeleteEndpoints(k8sEndpointSlice2.EndpointSliceID, swgEps)
  1223  	require.Nil(t, testutils.WaitUntil(func() bool {
  1224  		event := <-svcCache.Events
  1225  		defer event.SWG.Done()
  1226  		require.Equal(t, UpdateService, event.Action)
  1227  		require.Equal(t, svcID, event.ID)
  1228  		return true
  1229  	}, 2*time.Second))
  1230  
  1231  	endpoints, ready = svcCache.correlateEndpoints(svcID)
  1232  	require.Equal(t, true, ready)
  1233  	require.Equal(t, "2.2.2.2:8080/TCP", endpoints.String())
  1234  
  1235  	svcCache.DeleteEndpoints(k8sEndpointSlice1.EndpointSliceID, swgEps)
  1236  	require.Nil(t, testutils.WaitUntil(func() bool {
  1237  		event := <-svcCache.Events
  1238  		defer event.SWG.Done()
  1239  		require.Equal(t, UpdateService, event.Action)
  1240  		require.Equal(t, svcID, event.ID)
  1241  		return true
  1242  	}, 2*time.Second))
  1243  
  1244  	endpoints, serviceReady := svcCache.correlateEndpoints(svcID)
  1245  	require.Equal(t, false, serviceReady)
  1246  	require.Equal(t, "", endpoints.String())
  1247  
  1248  	// Reinserting the endpoints should re-match with the still existing service
  1249  	svcCache.UpdateEndpoints(k8sEndpointSlice1, swgEps)
  1250  	require.Nil(t, testutils.WaitUntil(func() bool {
  1251  		event := <-svcCache.Events
  1252  		defer event.SWG.Done()
  1253  		require.Equal(t, UpdateService, event.Action)
  1254  		require.Equal(t, svcID, event.ID)
  1255  		return true
  1256  	}, 2*time.Second))
  1257  
  1258  	endpoints, serviceReady = svcCache.correlateEndpoints(svcID)
  1259  	require.Equal(t, true, serviceReady)
  1260  	require.Equal(t, "2.2.2.2:8080/TCP", endpoints.String())
  1261  
  1262  	// Deleting the service will result in a service delete event
  1263  	svcCache.DeleteService(k8sSvc, swgSvcs)
  1264  	require.Nil(t, testutils.WaitUntil(func() bool {
  1265  		event := <-svcCache.Events
  1266  		defer event.SWG.Done()
  1267  		require.Equal(t, DeleteService, event.Action)
  1268  		require.Equal(t, svcID, event.ID)
  1269  		return true
  1270  	}, 2*time.Second))
  1271  
  1272  	// Deleting the endpoints will not emit an event as the notification
  1273  	// was sent out when the service was deleted.
  1274  	svcCache.DeleteEndpoints(k8sEndpointSlice1.EndpointSliceID, swgEps)
  1275  	time.Sleep(100 * time.Millisecond)
  1276  	select {
  1277  	case <-svcCache.Events:
  1278  		t.Error("Unexpected service delete event received")
  1279  	default:
  1280  	}
  1281  
  1282  	swgSvcs.Stop()
  1283  	require.Nil(t, testutils.WaitUntil(func() bool {
  1284  		swgSvcs.Wait()
  1285  		return true
  1286  	}, 2*time.Second))
  1287  
  1288  	swgEps.Stop()
  1289  	require.Nil(t, testutils.WaitUntil(func() bool {
  1290  		swgEps.Wait()
  1291  		return true
  1292  	}, 2*time.Second))
  1293  }
  1294  
  1295  func TestServiceCacheWith2EndpointSliceSameAddress(t *testing.T) {
  1296  	k8sEndpointSlice1 := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
  1297  		AddressType: slim_discovery_v1.AddressTypeIPv4,
  1298  		ObjectMeta: slim_metav1.ObjectMeta{
  1299  			Name:      "foo-yyyyy",
  1300  			Namespace: "bar",
  1301  			Labels: map[string]string{
  1302  				slim_discovery_v1.LabelServiceName: "foo",
  1303  			},
  1304  		},
  1305  		Endpoints: []slim_discovery_v1.Endpoint{
  1306  			{
  1307  				Addresses: []string{
  1308  					"2.2.2.2",
  1309  				},
  1310  			},
  1311  		},
  1312  		Ports: []slim_discovery_v1.EndpointPort{
  1313  			{
  1314  				Name:     func() *string { a := "http-test-svc"; return &a }(),
  1315  				Protocol: func() *slim_corev1.Protocol { a := slim_corev1.ProtocolTCP; return &a }(),
  1316  				Port:     func() *int32 { a := int32(8080); return &a }(),
  1317  			},
  1318  		},
  1319  	})
  1320  
  1321  	k8sEndpointSlice2 := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
  1322  		AddressType: slim_discovery_v1.AddressTypeIPv4,
  1323  		ObjectMeta: slim_metav1.ObjectMeta{
  1324  			Name:      "foo-xxxxx",
  1325  			Namespace: "bar",
  1326  			Labels: map[string]string{
  1327  				slim_discovery_v1.LabelServiceName: "foo",
  1328  			},
  1329  		},
  1330  		Endpoints: []slim_discovery_v1.Endpoint{
  1331  			{
  1332  				Addresses: []string{
  1333  					"2.2.2.2",
  1334  				},
  1335  			},
  1336  		},
  1337  		Ports: []slim_discovery_v1.EndpointPort{
  1338  			{
  1339  				Name:     func() *string { a := "http-test-svc2"; return &a }(),
  1340  				Protocol: func() *slim_corev1.Protocol { a := slim_corev1.ProtocolTCP; return &a }(),
  1341  				Port:     func() *int32 { a := int32(8081); return &a }(),
  1342  			},
  1343  		},
  1344  	})
  1345  
  1346  	db, nodeAddrs := newDB(t)
  1347  	svcCache := NewServiceCache(db, nodeAddrs)
  1348  
  1349  	k8sSvc := &slim_corev1.Service{
  1350  		ObjectMeta: slim_metav1.ObjectMeta{
  1351  			Name:      "foo",
  1352  			Namespace: "bar",
  1353  			Labels: map[string]string{
  1354  				"foo": "bar",
  1355  			},
  1356  		},
  1357  		Spec: slim_corev1.ServiceSpec{
  1358  			ClusterIP: "127.0.0.1",
  1359  			Selector: map[string]string{
  1360  				"foo": "bar",
  1361  			},
  1362  			Type: slim_corev1.ServiceTypeClusterIP,
  1363  		},
  1364  	}
  1365  
  1366  	swgSvcs := lock.NewStoppableWaitGroup()
  1367  	svcID := svcCache.UpdateService(k8sSvc, swgSvcs)
  1368  
  1369  	time.Sleep(100 * time.Millisecond)
  1370  
  1371  	select {
  1372  	case <-svcCache.Events:
  1373  		t.Error("Unexpected service event received before endpoints have been imported")
  1374  	default:
  1375  	}
  1376  
  1377  	swgEps := lock.NewStoppableWaitGroup()
  1378  	svcCache.UpdateEndpoints(k8sEndpointSlice1, swgEps)
  1379  	svcCache.UpdateEndpoints(k8sEndpointSlice2, swgEps)
  1380  
  1381  	// The service should be ready as both service and endpoints have been
  1382  	// imported for k8sEndpointSlice1
  1383  	require.Nil(t, testutils.WaitUntil(func() bool {
  1384  		event := <-svcCache.Events
  1385  		defer event.SWG.Done()
  1386  		require.Equal(t, UpdateService, event.Action)
  1387  		require.Equal(t, svcID, event.ID)
  1388  		return true
  1389  	}, 2*time.Second))
  1390  
  1391  	// The service should be ready as both service and endpoints have been
  1392  	// imported for k8sEndpointSlice2
  1393  	require.Nil(t, testutils.WaitUntil(func() bool {
  1394  		event := <-svcCache.Events
  1395  		defer event.SWG.Done()
  1396  		require.Equal(t, UpdateService, event.Action)
  1397  		require.Equal(t, svcID, event.ID)
  1398  		return true
  1399  	}, 2*time.Second))
  1400  
  1401  	select {
  1402  	case <-svcCache.Events:
  1403  		t.Error("Unexpected service event received when endpoints not selected by a service have been imported")
  1404  	default:
  1405  	}
  1406  	endpoints, ready := svcCache.correlateEndpoints(svcID)
  1407  	require.Equal(t, true, ready)
  1408  	require.Equal(t, "2.2.2.2:8080/TCP,2.2.2.2:8081/TCP", endpoints.String())
  1409  
  1410  	// Updating the service without changing it should not result in an event
  1411  	svcCache.UpdateService(k8sSvc, swgSvcs)
  1412  	time.Sleep(100 * time.Millisecond)
  1413  	select {
  1414  	case <-svcCache.Events:
  1415  		t.Error("Unexpected service event received for unchanged service object")
  1416  	default:
  1417  	}
  1418  
  1419  	// Deleting the service will result in a service delete event
  1420  	svcCache.DeleteService(k8sSvc, swgSvcs)
  1421  	require.Nil(t, testutils.WaitUntil(func() bool {
  1422  		event := <-svcCache.Events
  1423  		defer event.SWG.Done()
  1424  		require.Equal(t, DeleteService, event.Action)
  1425  		require.Equal(t, svcID, event.ID)
  1426  		return true
  1427  	}, 2*time.Second))
  1428  
  1429  	// Reinserting the service should re-match with the still existing endpoints
  1430  	svcCache.UpdateService(k8sSvc, swgSvcs)
  1431  	require.Nil(t, testutils.WaitUntil(func() bool {
  1432  		event := <-svcCache.Events
  1433  		defer event.SWG.Done()
  1434  		require.Equal(t, UpdateService, event.Action)
  1435  		require.Equal(t, svcID, event.ID)
  1436  		return true
  1437  	}, 2*time.Second))
  1438  
  1439  	// Deleting the k8sEndpointSlice2 will result in a service update event
  1440  	svcCache.DeleteEndpoints(k8sEndpointSlice2.EndpointSliceID, swgEps)
  1441  	require.Nil(t, testutils.WaitUntil(func() bool {
  1442  		event := <-svcCache.Events
  1443  		defer event.SWG.Done()
  1444  		require.Equal(t, UpdateService, event.Action)
  1445  		require.Equal(t, svcID, event.ID)
  1446  		return true
  1447  	}, 2*time.Second))
  1448  
  1449  	endpoints, ready = svcCache.correlateEndpoints(svcID)
  1450  	require.Equal(t, true, ready)
  1451  	require.Equal(t, "2.2.2.2:8080/TCP", endpoints.String())
  1452  
  1453  	svcCache.DeleteEndpoints(k8sEndpointSlice1.EndpointSliceID, swgEps)
  1454  	require.Nil(t, testutils.WaitUntil(func() bool {
  1455  		event := <-svcCache.Events
  1456  		defer event.SWG.Done()
  1457  		require.Equal(t, UpdateService, event.Action)
  1458  		require.Equal(t, svcID, event.ID)
  1459  		return true
  1460  	}, 2*time.Second))
  1461  
  1462  	endpoints, serviceReady := svcCache.correlateEndpoints(svcID)
  1463  	require.Equal(t, false, serviceReady)
  1464  	require.Equal(t, "", endpoints.String())
  1465  
  1466  	// Reinserting the endpoints should re-match with the still existing service
  1467  	svcCache.UpdateEndpoints(k8sEndpointSlice1, swgEps)
  1468  	require.Nil(t, testutils.WaitUntil(func() bool {
  1469  		event := <-svcCache.Events
  1470  		defer event.SWG.Done()
  1471  		require.Equal(t, UpdateService, event.Action)
  1472  		require.Equal(t, svcID, event.ID)
  1473  		return true
  1474  	}, 2*time.Second))
  1475  
  1476  	endpoints, serviceReady = svcCache.correlateEndpoints(svcID)
  1477  	require.Equal(t, true, serviceReady)
  1478  	require.Equal(t, "2.2.2.2:8080/TCP", endpoints.String())
  1479  
  1480  	// Deleting the service will result in a service delete event
  1481  	svcCache.DeleteService(k8sSvc, swgSvcs)
  1482  	require.Nil(t, testutils.WaitUntil(func() bool {
  1483  		event := <-svcCache.Events
  1484  		defer event.SWG.Done()
  1485  		require.Equal(t, DeleteService, event.Action)
  1486  		require.Equal(t, svcID, event.ID)
  1487  		return true
  1488  	}, 2*time.Second))
  1489  
  1490  	// Deleting the endpoints will not emit an event as the notification
  1491  	// was sent out when the service was deleted.
  1492  	svcCache.DeleteEndpoints(k8sEndpointSlice1.EndpointSliceID, swgEps)
  1493  	time.Sleep(100 * time.Millisecond)
  1494  	select {
  1495  	case <-svcCache.Events:
  1496  		t.Error("Unexpected service delete event received")
  1497  	default:
  1498  	}
  1499  
  1500  	swgSvcs.Stop()
  1501  	require.Nil(t, testutils.WaitUntil(func() bool {
  1502  		swgSvcs.Wait()
  1503  		return true
  1504  	}, 2*time.Second))
  1505  
  1506  	swgEps.Stop()
  1507  	require.Nil(t, testutils.WaitUntil(func() bool {
  1508  		swgEps.Wait()
  1509  		return true
  1510  	}, 2*time.Second))
  1511  }
  1512  
  1513  func TestServiceEndpointFiltering(t *testing.T) {
  1514  	k8sSvc := &slim_corev1.Service{
  1515  		ObjectMeta: slim_metav1.ObjectMeta{
  1516  			Name:      "foo",
  1517  			Namespace: "bar",
  1518  			Labels:    map[string]string{"foo": "bar"},
  1519  			Annotations: map[string]string{
  1520  				v1.DeprecatedAnnotationTopologyAwareHints: "auto",
  1521  			},
  1522  		},
  1523  		Spec: slim_corev1.ServiceSpec{
  1524  			ClusterIP: "127.0.0.1",
  1525  			Selector:  map[string]string{"foo": "bar"},
  1526  			Type:      slim_corev1.ServiceTypeClusterIP,
  1527  		},
  1528  	}
  1529  	veryTrue := true
  1530  	k8sEndpointSlice := ParseEndpointSliceV1(&slim_discovery_v1.EndpointSlice{
  1531  		AddressType: slim_discovery_v1.AddressTypeIPv4,
  1532  		ObjectMeta: slim_metav1.ObjectMeta{
  1533  			Name:      "foo-ep-filtering",
  1534  			Namespace: "bar",
  1535  			Labels: map[string]string{
  1536  				slim_discovery_v1.LabelServiceName: "foo",
  1537  			},
  1538  		},
  1539  		Endpoints: []slim_discovery_v1.Endpoint{
  1540  			{
  1541  				Addresses: []string{"10.0.0.1"},
  1542  				Hints: &slim_discovery_v1.EndpointHints{
  1543  					ForZones: []slim_discovery_v1.ForZone{{Name: "test-zone-1"}},
  1544  				},
  1545  				Conditions: slim_discovery_v1.EndpointConditions{Ready: &veryTrue},
  1546  			},
  1547  			{
  1548  				Addresses: []string{"10.0.0.2"},
  1549  				Hints: &slim_discovery_v1.EndpointHints{
  1550  					ForZones: []slim_discovery_v1.ForZone{{Name: "test-zone-2"}},
  1551  				},
  1552  				Conditions: slim_discovery_v1.EndpointConditions{Ready: &veryTrue},
  1553  			},
  1554  		},
  1555  	})
  1556  
  1557  	store := node.NewTestLocalNodeStore(node.LocalNode{Node: types.Node{
  1558  		Labels: map[string]string{v1.LabelTopologyZone: "test-zone-2"},
  1559  	}})
  1560  	db, nodeAddrs := newDB(t)
  1561  	svcCache := newServiceCache(hivetest.Lifecycle(t),
  1562  		ServiceCacheConfig{EnableServiceTopology: true}, store,
  1563  		db, nodeAddrs)
  1564  
  1565  	swg := lock.NewStoppableWaitGroup()
  1566  
  1567  	// Now update service and endpointslice. This should result in the service
  1568  	// update with 2.2.2.2 endpoint due to the zone filtering.
  1569  	svcID0 := svcCache.UpdateService(k8sSvc, swg)
  1570  	svcID1, eps := svcCache.UpdateEndpoints(k8sEndpointSlice, swg)
  1571  	require.Equal(t, svcID1, svcID0)
  1572  	require.Equal(t, 1, len(eps.Backends))
  1573  	require.Nil(t, testutils.WaitUntil(func() bool {
  1574  		event := <-svcCache.Events
  1575  		require.Equal(t, UpdateService, event.Action)
  1576  		require.Equal(t, svcID0, event.ID)
  1577  		require.Equal(t, 1, len(event.Endpoints.Backends))
  1578  		_, found := event.Endpoints.Backends[cmtypes.MustParseAddrCluster("10.0.0.2")]
  1579  		require.Equal(t, true, found)
  1580  		return true
  1581  	}, 2*time.Second))
  1582  
  1583  	// Send self node update to remove the node's zone label. This should
  1584  	// generate the service update with both endpoints selected
  1585  	store.Update(func(ln *node.LocalNode) { ln.Labels = nil })
  1586  	require.Nil(t, testutils.WaitUntil(func() bool {
  1587  		event := <-svcCache.Events
  1588  		require.Equal(t, UpdateService, event.Action)
  1589  		require.Equal(t, svcID0, event.ID)
  1590  		require.Equal(t, 2, len(event.Endpoints.Backends))
  1591  		return true
  1592  	}, 2*time.Second))
  1593  
  1594  	// Set the node's zone to test-zone-1 to select the first endpoint
  1595  	store.Update(func(ln *node.LocalNode) { ln.Labels = map[string]string{v1.LabelTopologyZone: "test-zone-1"} })
  1596  	require.Nil(t, testutils.WaitUntil(func() bool {
  1597  		event := <-svcCache.Events
  1598  		require.Equal(t, UpdateService, event.Action)
  1599  		require.Equal(t, svcID0, event.ID)
  1600  		require.Equal(t, 1, len(event.Endpoints.Backends))
  1601  		_, found := event.Endpoints.Backends[cmtypes.MustParseAddrCluster("10.0.0.1")]
  1602  		require.Equal(t, true, found)
  1603  		return true
  1604  	}, 2*time.Second))
  1605  
  1606  	// Removing the service annotation should have no effect as long as EndpointSlice hints are set
  1607  	k8sSvc.ObjectMeta.Annotations = nil
  1608  	svcID0 = svcCache.UpdateService(k8sSvc, swg)
  1609  	require.Nil(t, testutils.WaitUntil(func() bool {
  1610  		event := <-svcCache.Events
  1611  		require.Equal(t, UpdateService, event.Action)
  1612  		require.Equal(t, svcID0, event.ID)
  1613  		require.Equal(t, 1, len(event.Endpoints.Backends))
  1614  		return true
  1615  	}, 2*time.Second))
  1616  
  1617  	// Remove the zone hints. This should select all endpoints
  1618  	k8sEndpointSlice = k8sEndpointSlice.DeepCopy()
  1619  	for _, be := range k8sEndpointSlice.Backends {
  1620  		be.HintsForZones = nil
  1621  	}
  1622  	svcID1, _ = svcCache.UpdateEndpoints(k8sEndpointSlice, swg)
  1623  	require.Nil(t, testutils.WaitUntil(func() bool {
  1624  		event := <-svcCache.Events
  1625  		require.Equal(t, UpdateService, event.Action)
  1626  		require.Equal(t, svcID1, event.ID)
  1627  		require.Equal(t, 2, len(event.Endpoints.Backends))
  1628  		return true
  1629  	}, 2*time.Second))
  1630  }