github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconcilerv2/pod_ip_pool_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package reconcilerv2
     5  
     6  import (
     7  	"context"
     8  	"net/netip"
     9  	"testing"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	"github.com/stretchr/testify/require"
    13  	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/utils/ptr"
    15  
    16  	"github.com/cilium/cilium/pkg/bgpv1/manager/instance"
    17  	"github.com/cilium/cilium/pkg/bgpv1/manager/store"
    18  	"github.com/cilium/cilium/pkg/bgpv1/types"
    19  	ipamtypes "github.com/cilium/cilium/pkg/ipam/types"
    20  	v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    21  	"github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    22  	"github.com/cilium/cilium/pkg/k8s/resource"
    23  	slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    24  )
    25  
    26  var (
    27  	podIPPoolTestLogger = logrus.WithField("unit_test", "reconcilerv2_podippool")
    28  )
    29  
    30  var (
    31  	redPoolCIDRv4        = v2alpha1.PoolCIDR("10.0.0.0/16")
    32  	redPoolCIDRv6        = v2alpha1.PoolCIDR("2001:db8::/64")
    33  	redPoolNodePrefix1v4 = ipamtypes.IPAMPodCIDR("10.0.1.0/24")
    34  	redPoolNodePrefix2v4 = ipamtypes.IPAMPodCIDR("10.0.2.0/24")
    35  	redPoolNodePrefix1v6 = ipamtypes.IPAMPodCIDR("2001:db8:0:0:1234::/96")
    36  	redPoolNodePrefix2v6 = ipamtypes.IPAMPodCIDR("2001:db8:0:0:1235::/96")
    37  
    38  	redPoolName       = "red-pool"
    39  	redLabelSelector  = slimv1.LabelSelector{MatchLabels: map[string]string{"pool": "red"}}
    40  	redNameNSSelector = slimv1.LabelSelector{MatchLabels: map[string]string{
    41  		podIPPoolNameLabel: redPoolName,
    42  	}}
    43  	redPool = &v2alpha1.CiliumPodIPPool{
    44  		ObjectMeta: metaV1.ObjectMeta{
    45  			Name:   redPoolName,
    46  			Labels: redLabelSelector.MatchLabels,
    47  		},
    48  		Spec: v2alpha1.IPPoolSpec{
    49  			IPv4: &v2alpha1.IPv4PoolSpec{
    50  				CIDRs:    []v2alpha1.PoolCIDR{redPoolCIDRv4},
    51  				MaskSize: 24,
    52  			},
    53  			IPv6: &v2alpha1.IPv6PoolSpec{
    54  				CIDRs:    []v2alpha1.PoolCIDR{redPoolCIDRv6},
    55  				MaskSize: 96,
    56  			},
    57  		},
    58  	}
    59  	redPeer65001v4PodIPPoolRPName = PolicyName("red-peer-65001", "ipv4", v2alpha1.BGPCiliumPodIPPoolAdvert, redPoolName)
    60  	redPeer65001v4PodIPPoolRP     = &types.RoutePolicy{
    61  		Name: redPeer65001v4PodIPPoolRPName,
    62  		Type: types.RoutePolicyTypeExport,
    63  		Statements: []*types.RoutePolicyStatement{
    64  			{
    65  				Conditions: types.RoutePolicyConditions{
    66  					MatchNeighbors: []string{"10.10.10.1/32"},
    67  					MatchPrefixes: []*types.RoutePolicyPrefixMatch{
    68  						{
    69  							CIDR:         netip.MustParsePrefix(string(redPoolNodePrefix1v4)),
    70  							PrefixLenMin: 24,
    71  							PrefixLenMax: 24,
    72  						},
    73  						{
    74  							CIDR:         netip.MustParsePrefix(string(redPoolNodePrefix2v4)),
    75  							PrefixLenMin: 24,
    76  							PrefixLenMax: 24,
    77  						},
    78  					},
    79  				},
    80  				Actions: types.RoutePolicyActions{
    81  					RouteAction:    types.RoutePolicyActionAccept,
    82  					AddCommunities: []string{"65000:200"},
    83  				},
    84  			},
    85  		},
    86  	}
    87  	redPeer65001v6PodIPPoolRPName = PolicyName("red-peer-65001", "ipv6", v2alpha1.BGPCiliumPodIPPoolAdvert, redPoolName)
    88  	redPeer65001v6PodIPPoolRP     = &types.RoutePolicy{
    89  		Name: redPeer65001v6PodIPPoolRPName,
    90  		Type: types.RoutePolicyTypeExport,
    91  		Statements: []*types.RoutePolicyStatement{
    92  			{
    93  				Conditions: types.RoutePolicyConditions{
    94  					MatchNeighbors: []string{"10.10.10.1/32"},
    95  					MatchPrefixes: []*types.RoutePolicyPrefixMatch{
    96  						{
    97  							CIDR:         netip.MustParsePrefix(string(redPoolNodePrefix1v6)),
    98  							PrefixLenMin: 96,
    99  							PrefixLenMax: 96,
   100  						},
   101  						{
   102  							CIDR:         netip.MustParsePrefix(string(redPoolNodePrefix2v6)),
   103  							PrefixLenMin: 96,
   104  							PrefixLenMax: 96,
   105  						},
   106  					},
   107  				},
   108  				Actions: types.RoutePolicyActions{
   109  					RouteAction:    types.RoutePolicyActionAccept,
   110  					AddCommunities: []string{"65000:200"},
   111  				},
   112  			},
   113  		},
   114  	}
   115  
   116  	bluePoolCIDR1v4       = v2alpha1.PoolCIDR("10.1.0.0/16")
   117  	bluePoolNodePrefix1v4 = ipamtypes.IPAMPodCIDR("10.1.1.0/24")
   118  	bluePoolCIDR2v4       = v2alpha1.PoolCIDR("10.2.0.0/16")
   119  	bluePoolNodePrefix2v4 = ipamtypes.IPAMPodCIDR("10.2.1.0/24")
   120  	bluePoolCIDR3v4       = v2alpha1.PoolCIDR("10.3.0.0/16")
   121  	bluePoolCIDR1v6       = v2alpha1.PoolCIDR("2001:db8:1::/64")
   122  	bluePoolNodePrefix1v6 = ipamtypes.IPAMPodCIDR("2001:db8:1:0:1234::/96")
   123  	bluePoolCIDR2v6       = v2alpha1.PoolCIDR("2001:db8:2::/64")
   124  	bluePoolNodePrefix2v6 = ipamtypes.IPAMPodCIDR("2001:db8:2:0:1234::/96")
   125  	bluePoolCIDR3v6       = v2alpha1.PoolCIDR("2001:db8:3::/64")
   126  
   127  	bluePoolName       = "blue-pool"
   128  	blueLabelSelector  = slimv1.LabelSelector{MatchLabels: map[string]string{"pool": "blue"}}
   129  	blueNameNSSelector = slimv1.LabelSelector{MatchLabels: map[string]string{
   130  		podIPPoolNameLabel: bluePoolName,
   131  	}}
   132  	bluePool = &v2alpha1.CiliumPodIPPool{
   133  		ObjectMeta: metaV1.ObjectMeta{
   134  			Name:   bluePoolName,
   135  			Labels: blueLabelSelector.MatchLabels,
   136  		},
   137  		Spec: v2alpha1.IPPoolSpec{
   138  			IPv4: &v2alpha1.IPv4PoolSpec{
   139  				CIDRs: []v2alpha1.PoolCIDR{
   140  					bluePoolCIDR1v4,
   141  					bluePoolCIDR2v4,
   142  					bluePoolCIDR3v4,
   143  				},
   144  				MaskSize: 24,
   145  			},
   146  			IPv6: &v2alpha1.IPv6PoolSpec{
   147  				CIDRs: []v2alpha1.PoolCIDR{
   148  					bluePoolCIDR1v6,
   149  					bluePoolCIDR2v6,
   150  					bluePoolCIDR3v6,
   151  				},
   152  				MaskSize: 96,
   153  			},
   154  		},
   155  	}
   156  	bluePeer65001v4PodIPPoolRPName = PolicyName("blue-peer-65001", "ipv4", v2alpha1.BGPCiliumPodIPPoolAdvert, bluePoolName)
   157  	bluePeer65001v4PodIPPoolRP     = &types.RoutePolicy{
   158  		Name: bluePeer65001v4PodIPPoolRPName,
   159  		Type: types.RoutePolicyTypeExport,
   160  		Statements: []*types.RoutePolicyStatement{
   161  			{
   162  				Conditions: types.RoutePolicyConditions{
   163  					MatchNeighbors: []string{"10.10.10.2/32"},
   164  					MatchPrefixes: []*types.RoutePolicyPrefixMatch{
   165  						{
   166  							CIDR:         netip.MustParsePrefix(string(bluePoolNodePrefix1v4)),
   167  							PrefixLenMin: 24,
   168  							PrefixLenMax: 24,
   169  						},
   170  						{
   171  							CIDR:         netip.MustParsePrefix(string(bluePoolNodePrefix2v4)),
   172  							PrefixLenMin: 24,
   173  							PrefixLenMax: 24,
   174  						},
   175  					},
   176  				},
   177  				Actions: types.RoutePolicyActions{
   178  					RouteAction:    types.RoutePolicyActionAccept,
   179  					AddCommunities: []string{"65355:200"},
   180  				},
   181  			},
   182  		},
   183  	}
   184  	bluePeer65001v6PodIPPoolRPName = PolicyName("blue-peer-65001", "ipv6", v2alpha1.BGPCiliumPodIPPoolAdvert, bluePoolName)
   185  	bluePeer65001v6PodIPPoolRP     = &types.RoutePolicy{
   186  		Name: bluePeer65001v6PodIPPoolRPName,
   187  		Type: types.RoutePolicyTypeExport,
   188  		Statements: []*types.RoutePolicyStatement{
   189  			{
   190  				Conditions: types.RoutePolicyConditions{
   191  					MatchNeighbors: []string{"10.10.10.2/32"},
   192  					MatchPrefixes: []*types.RoutePolicyPrefixMatch{
   193  						{
   194  							CIDR:         netip.MustParsePrefix(string(bluePoolNodePrefix1v6)),
   195  							PrefixLenMin: 96,
   196  							PrefixLenMax: 96,
   197  						},
   198  						{
   199  							CIDR:         netip.MustParsePrefix(string(bluePoolNodePrefix2v6)),
   200  							PrefixLenMin: 96,
   201  							PrefixLenMax: 96,
   202  						},
   203  					},
   204  				},
   205  				Actions: types.RoutePolicyActions{
   206  					RouteAction:    types.RoutePolicyActionAccept,
   207  					AddCommunities: []string{"65355:200"},
   208  				},
   209  			},
   210  		},
   211  	}
   212  )
   213  
   214  func Test_PodIPPoolAdvertisements(t *testing.T) {
   215  	logrus.SetLevel(logrus.DebugLevel)
   216  
   217  	tests := []struct {
   218  		name                     string
   219  		peerConfig               []*v2alpha1.CiliumBGPPeerConfig
   220  		advertisements           []*v2alpha1.CiliumBGPAdvertisement
   221  		pools                    []*v2alpha1.CiliumPodIPPool
   222  		preconfiguredPoolAFPaths map[resource.Key]map[types.Family]map[string]struct{}
   223  		preconfiguredRPs         ResourceRoutePolicyMap
   224  		testCiliumNode           *v2api.CiliumNode
   225  		testBGPInstanceConfig    *v2alpha1.CiliumBGPNodeInstance
   226  		expectedPoolAFPaths      map[resource.Key]map[types.Family]map[string]struct{}
   227  		expectedRPs              ResourceRoutePolicyMap
   228  	}{
   229  		{
   230  			name: "dual stack, advertisement selects pools (by label), pool present on the node",
   231  			peerConfig: []*v2alpha1.CiliumBGPPeerConfig{
   232  				redPeerConfig,
   233  				bluePeerConfig,
   234  			},
   235  			advertisements: []*v2alpha1.CiliumBGPAdvertisement{
   236  				redAdvertWithSelector(&redLabelSelector),
   237  				blueAdvertWithSelector(&blueLabelSelector),
   238  			},
   239  			pools: []*v2alpha1.CiliumPodIPPool{
   240  				redPool,
   241  				bluePool,
   242  			},
   243  			preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{},
   244  			preconfiguredRPs:         ResourceRoutePolicyMap{},
   245  			testCiliumNode: &v2api.CiliumNode{
   246  				ObjectMeta: metaV1.ObjectMeta{
   247  					Name: "Test Node",
   248  				},
   249  				Spec: v2api.NodeSpec{
   250  					IPAM: ipamtypes.IPAMSpec{
   251  						Pools: ipamtypes.IPAMPoolSpec{
   252  							Allocated: []ipamtypes.IPAMPoolAllocation{
   253  								{
   254  									Pool: redPoolName,
   255  									CIDRs: []ipamtypes.IPAMPodCIDR{
   256  										redPoolNodePrefix1v4,
   257  										redPoolNodePrefix2v4,
   258  										redPoolNodePrefix1v6,
   259  										redPoolNodePrefix2v6,
   260  									},
   261  								},
   262  								{
   263  									Pool: bluePoolName,
   264  									CIDRs: []ipamtypes.IPAMPodCIDR{
   265  										bluePoolNodePrefix1v4,
   266  										bluePoolNodePrefix2v4,
   267  										bluePoolNodePrefix1v6,
   268  										bluePoolNodePrefix2v6,
   269  									},
   270  								},
   271  							},
   272  						},
   273  					},
   274  				},
   275  			},
   276  
   277  			testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{
   278  				Name:     "bgp-65001",
   279  				LocalASN: ptr.To[int64](65001),
   280  				Peers:    []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001},
   281  			},
   282  			expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{
   283  				{Name: redPoolName}: {
   284  					{Afi: types.AfiIPv4, Safi: types.SafiUnicast}: {
   285  						string(redPoolNodePrefix1v4): struct{}{},
   286  						string(redPoolNodePrefix2v4): struct{}{},
   287  					},
   288  					{Afi: types.AfiIPv6, Safi: types.SafiUnicast}: {
   289  						string(redPoolNodePrefix1v6): struct{}{},
   290  						string(redPoolNodePrefix2v6): struct{}{},
   291  					},
   292  				},
   293  				{Name: bluePoolName}: {
   294  					{Afi: types.AfiIPv4, Safi: types.SafiUnicast}: {
   295  						string(bluePoolNodePrefix1v4): struct{}{},
   296  						string(bluePoolNodePrefix2v4): struct{}{},
   297  					},
   298  					{Afi: types.AfiIPv6, Safi: types.SafiUnicast}: {
   299  						string(bluePoolNodePrefix1v6): struct{}{},
   300  						string(bluePoolNodePrefix2v6): struct{}{},
   301  					},
   302  				},
   303  			},
   304  			expectedRPs: ResourceRoutePolicyMap{
   305  				resource.Key{Name: redPoolName}: RoutePolicyMap{
   306  					redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP,
   307  					redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP,
   308  				},
   309  				resource.Key{Name: bluePoolName}: RoutePolicyMap{
   310  					bluePeer65001v4PodIPPoolRPName: bluePeer65001v4PodIPPoolRP,
   311  					bluePeer65001v6PodIPPoolRPName: bluePeer65001v6PodIPPoolRP,
   312  				},
   313  			},
   314  		},
   315  		{
   316  			name: "dual stack, advertisement selects pools (by nameNS selector), pool present on the node",
   317  			peerConfig: []*v2alpha1.CiliumBGPPeerConfig{
   318  				redPeerConfig,
   319  				bluePeerConfig,
   320  			},
   321  			advertisements: []*v2alpha1.CiliumBGPAdvertisement{
   322  				redAdvertWithSelector(&redNameNSSelector),
   323  				blueAdvertWithSelector(&blueNameNSSelector),
   324  			},
   325  			pools: []*v2alpha1.CiliumPodIPPool{
   326  				redPool,
   327  				bluePool,
   328  			},
   329  			preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{},
   330  			preconfiguredRPs:         ResourceRoutePolicyMap{},
   331  			testCiliumNode: &v2api.CiliumNode{
   332  				ObjectMeta: metaV1.ObjectMeta{
   333  					Name: "Test Node",
   334  				},
   335  				Spec: v2api.NodeSpec{
   336  					IPAM: ipamtypes.IPAMSpec{
   337  						Pools: ipamtypes.IPAMPoolSpec{
   338  							Allocated: []ipamtypes.IPAMPoolAllocation{
   339  								{
   340  									Pool: redPoolName,
   341  									CIDRs: []ipamtypes.IPAMPodCIDR{
   342  										redPoolNodePrefix1v4,
   343  										redPoolNodePrefix2v4,
   344  										redPoolNodePrefix1v6,
   345  										redPoolNodePrefix2v6,
   346  									},
   347  								},
   348  								{
   349  									Pool: bluePoolName,
   350  									CIDRs: []ipamtypes.IPAMPodCIDR{
   351  										bluePoolNodePrefix1v4,
   352  										bluePoolNodePrefix2v4,
   353  										bluePoolNodePrefix1v6,
   354  										bluePoolNodePrefix2v6,
   355  									},
   356  								},
   357  							},
   358  						},
   359  					},
   360  				},
   361  			},
   362  
   363  			testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{
   364  				Name:     "bgp-65001",
   365  				LocalASN: ptr.To[int64](65001),
   366  				Peers:    []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001},
   367  			},
   368  			expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{
   369  				{Name: redPoolName}: {
   370  					{Afi: types.AfiIPv4, Safi: types.SafiUnicast}: {
   371  						string(redPoolNodePrefix1v4): struct{}{},
   372  						string(redPoolNodePrefix2v4): struct{}{},
   373  					},
   374  					{Afi: types.AfiIPv6, Safi: types.SafiUnicast}: {
   375  						string(redPoolNodePrefix1v6): struct{}{},
   376  						string(redPoolNodePrefix2v6): struct{}{},
   377  					},
   378  				},
   379  				{Name: bluePoolName}: {
   380  					{Afi: types.AfiIPv4, Safi: types.SafiUnicast}: {
   381  						string(bluePoolNodePrefix1v4): struct{}{},
   382  						string(bluePoolNodePrefix2v4): struct{}{},
   383  					},
   384  					{Afi: types.AfiIPv6, Safi: types.SafiUnicast}: {
   385  						string(bluePoolNodePrefix1v6): struct{}{},
   386  						string(bluePoolNodePrefix2v6): struct{}{},
   387  					},
   388  				},
   389  			},
   390  			expectedRPs: ResourceRoutePolicyMap{
   391  				resource.Key{Name: redPoolName}: RoutePolicyMap{
   392  					redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP,
   393  					redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP,
   394  				},
   395  				resource.Key{Name: bluePoolName}: RoutePolicyMap{
   396  					bluePeer65001v4PodIPPoolRPName: bluePeer65001v4PodIPPoolRP,
   397  					bluePeer65001v6PodIPPoolRPName: bluePeer65001v6PodIPPoolRP,
   398  				},
   399  			},
   400  		},
   401  		{
   402  			name: "dual stack, pool NOT selected by advertisement, pool present on the node",
   403  			peerConfig: []*v2alpha1.CiliumBGPPeerConfig{
   404  				redPeerConfig,
   405  				bluePeerConfig,
   406  			},
   407  			advertisements: []*v2alpha1.CiliumBGPAdvertisement{
   408  				redAdvert,  // no selector matching red pool
   409  				blueAdvert, // no selector matching blue pool
   410  			},
   411  			pools: []*v2alpha1.CiliumPodIPPool{
   412  				redPool,
   413  				bluePool,
   414  			},
   415  			preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{},
   416  			testCiliumNode: &v2api.CiliumNode{
   417  				ObjectMeta: metaV1.ObjectMeta{
   418  					Name: "Test Node",
   419  				},
   420  				Spec: v2api.NodeSpec{
   421  					IPAM: ipamtypes.IPAMSpec{
   422  						Pools: ipamtypes.IPAMPoolSpec{
   423  							Allocated: []ipamtypes.IPAMPoolAllocation{
   424  								{
   425  									Pool: redPoolName,
   426  									CIDRs: []ipamtypes.IPAMPodCIDR{
   427  										redPoolNodePrefix1v4,
   428  										redPoolNodePrefix2v4,
   429  										redPoolNodePrefix1v6,
   430  										redPoolNodePrefix2v6,
   431  									},
   432  								},
   433  								{
   434  									Pool: bluePoolName,
   435  									CIDRs: []ipamtypes.IPAMPodCIDR{
   436  										bluePoolNodePrefix1v4,
   437  										bluePoolNodePrefix2v4,
   438  										bluePoolNodePrefix1v6,
   439  										bluePoolNodePrefix2v6,
   440  									},
   441  								},
   442  							},
   443  						},
   444  					},
   445  				},
   446  			},
   447  			testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{
   448  				Name:     "bgp-65001",
   449  				LocalASN: ptr.To[int64](65001),
   450  				Peers:    []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001},
   451  			},
   452  			expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{},
   453  			expectedRPs:         nil,
   454  		},
   455  		{
   456  			name: "dual stack, pool selected by advertisement, pool NOT present on the node",
   457  			peerConfig: []*v2alpha1.CiliumBGPPeerConfig{
   458  				redPeerConfig,
   459  				bluePeerConfig,
   460  			},
   461  			advertisements: []*v2alpha1.CiliumBGPAdvertisement{
   462  				redAdvertWithSelector(&redLabelSelector),
   463  				blueAdvertWithSelector(&blueLabelSelector),
   464  			},
   465  			pools: []*v2alpha1.CiliumPodIPPool{
   466  				redPool,
   467  				bluePool,
   468  			},
   469  			preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{},
   470  			testCiliumNode: &v2api.CiliumNode{
   471  				ObjectMeta: metaV1.ObjectMeta{
   472  					Name: "Test Node",
   473  				},
   474  				Spec: v2api.NodeSpec{
   475  					IPAM: ipamtypes.IPAMSpec{
   476  						Pools: ipamtypes.IPAMPoolSpec{
   477  							Allocated: []ipamtypes.IPAMPoolAllocation{
   478  								{
   479  									Pool:  redPoolName,
   480  									CIDRs: []ipamtypes.IPAMPodCIDR{},
   481  								},
   482  								{
   483  									Pool:  bluePoolName,
   484  									CIDRs: []ipamtypes.IPAMPodCIDR{},
   485  								},
   486  							},
   487  						},
   488  					},
   489  				},
   490  			},
   491  
   492  			testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{
   493  				Name:     "bgp-65001",
   494  				LocalASN: ptr.To[int64](65001),
   495  				Peers:    []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001},
   496  			},
   497  			expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{},
   498  			expectedRPs:         nil,
   499  		},
   500  		{
   501  			name: "dual stack, clean up of preconfigured advertisements",
   502  			peerConfig: []*v2alpha1.CiliumBGPPeerConfig{
   503  				redPeerConfig,
   504  				bluePeerConfig,
   505  			},
   506  			advertisements: []*v2alpha1.CiliumBGPAdvertisement{
   507  				redAdvertWithSelector(&redLabelSelector),
   508  			},
   509  			pools: []*v2alpha1.CiliumPodIPPool{
   510  				redPool,
   511  			},
   512  			preconfiguredPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{
   513  				{Name: "unknown"}: {
   514  					{Afi: types.AfiIPv4, Safi: types.SafiUnicast}: {
   515  						"10.10.1.0/24": struct{}{},
   516  						"10.10.2.0/24": struct{}{},
   517  					},
   518  					{Afi: types.AfiIPv6, Safi: types.SafiUnicast}: {
   519  						"2001:db8:100:0:1234::/96": struct{}{},
   520  						"2001:db8:101:0:1234::/96": struct{}{},
   521  					},
   522  				},
   523  			},
   524  			preconfiguredRPs: ResourceRoutePolicyMap{
   525  				resource.Key{Name: redPoolName}: RoutePolicyMap{
   526  					redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP,
   527  					redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP,
   528  				},
   529  				resource.Key{Name: bluePoolName}: RoutePolicyMap{
   530  					bluePeer65001v4PodIPPoolRPName: bluePeer65001v4PodIPPoolRP,
   531  					bluePeer65001v6PodIPPoolRPName: bluePeer65001v6PodIPPoolRP,
   532  				},
   533  			},
   534  			testCiliumNode: &v2api.CiliumNode{
   535  				ObjectMeta: metaV1.ObjectMeta{
   536  					Name: "Test Node",
   537  				},
   538  				Spec: v2api.NodeSpec{
   539  					IPAM: ipamtypes.IPAMSpec{
   540  						Pools: ipamtypes.IPAMPoolSpec{
   541  							Allocated: []ipamtypes.IPAMPoolAllocation{
   542  								{
   543  									Pool: redPoolName,
   544  									CIDRs: []ipamtypes.IPAMPodCIDR{
   545  										redPoolNodePrefix1v4,
   546  										redPoolNodePrefix2v4,
   547  										redPoolNodePrefix1v6,
   548  										redPoolNodePrefix2v6,
   549  									},
   550  								},
   551  							},
   552  						},
   553  					},
   554  				},
   555  			},
   556  
   557  			testBGPInstanceConfig: &v2alpha1.CiliumBGPNodeInstance{
   558  				Name:     "bgp-65001",
   559  				LocalASN: ptr.To[int64](65001),
   560  				Peers:    []v2alpha1.CiliumBGPNodePeer{redPeer65001, bluePeer65001},
   561  			},
   562  			expectedPoolAFPaths: map[resource.Key]map[types.Family]map[string]struct{}{
   563  				{Name: redPoolName}: {
   564  					{Afi: types.AfiIPv4, Safi: types.SafiUnicast}: {
   565  						string(redPoolNodePrefix1v4): struct{}{},
   566  						string(redPoolNodePrefix2v4): struct{}{},
   567  					},
   568  					{Afi: types.AfiIPv6, Safi: types.SafiUnicast}: {
   569  						string(redPoolNodePrefix1v6): struct{}{},
   570  						string(redPoolNodePrefix2v6): struct{}{},
   571  					},
   572  				},
   573  			},
   574  			expectedRPs: ResourceRoutePolicyMap{
   575  				resource.Key{Name: redPoolName}: RoutePolicyMap{
   576  					redPeer65001v4PodIPPoolRPName: redPeer65001v4PodIPPoolRP,
   577  					redPeer65001v6PodIPPoolRPName: redPeer65001v6PodIPPoolRP,
   578  				},
   579  			},
   580  		},
   581  	}
   582  
   583  	for _, tt := range tests {
   584  		t.Run(tt.name, func(t *testing.T) {
   585  			req := require.New(t)
   586  
   587  			params := PodIPPoolReconcilerIn{
   588  				Logger: podIPPoolTestLogger,
   589  				PeerAdvert: NewCiliumPeerAdvertisement(
   590  					PeerAdvertisementIn{
   591  						Logger:          podCIDRTestLogger,
   592  						PeerConfigStore: store.InitMockStore[*v2alpha1.CiliumBGPPeerConfig](tt.peerConfig),
   593  						AdvertStore:     store.InitMockStore[*v2alpha1.CiliumBGPAdvertisement](tt.advertisements),
   594  					}),
   595  				PoolStore: store.InitMockStore[*v2alpha1.CiliumPodIPPool](tt.pools),
   596  			}
   597  			podIPPoolReconciler := NewPodIPPoolReconciler(params).Reconciler.(*PodIPPoolReconciler)
   598  
   599  			testBGPInstance := instance.NewFakeBGPInstance()
   600  
   601  			// set the preconfigured advertisements
   602  			presetPoolAFPaths := make(ResourceAFPathsMap)
   603  			for pool, prePoolAFPaths := range tt.preconfiguredPoolAFPaths {
   604  				presetPoolAFPaths[pool] = make(AFPathsMap)
   605  				for fam, afPaths := range prePoolAFPaths {
   606  					pathSet := make(PathMap)
   607  					for prePath := range afPaths {
   608  						path := types.NewPathForPrefix(netip.MustParsePrefix(prePath))
   609  						path.Family = fam
   610  						pathSet[prePath] = path
   611  					}
   612  					presetPoolAFPaths[pool][fam] = pathSet
   613  				}
   614  			}
   615  			podIPPoolReconciler.setMetadata(testBGPInstance, PodIPPoolReconcilerMetadata{
   616  				PoolAFPaths:       presetPoolAFPaths,
   617  				PoolRoutePolicies: tt.preconfiguredRPs,
   618  			})
   619  
   620  			// run podIPPoolReconciler twice to ensure idempotency
   621  			for i := 0; i < 2; i++ {
   622  				err := podIPPoolReconciler.Reconcile(context.Background(), ReconcileParams{
   623  					BGPInstance:   testBGPInstance,
   624  					DesiredConfig: tt.testBGPInstanceConfig,
   625  					CiliumNode:    tt.testCiliumNode,
   626  				})
   627  				req.NoError(err)
   628  			}
   629  
   630  			// check if the advertisements are as expected
   631  			runningPoolAFPaths := make(map[resource.Key]map[types.Family]map[string]struct{})
   632  			for pool, poolAFPaths := range podIPPoolReconciler.getMetadata(testBGPInstance).PoolAFPaths {
   633  				runningPoolAFPaths[pool] = make(map[types.Family]map[string]struct{})
   634  				for fam, afPaths := range poolAFPaths {
   635  					pathSet := make(map[string]struct{})
   636  					for pathKey := range afPaths {
   637  						pathSet[pathKey] = struct{}{}
   638  					}
   639  					runningPoolAFPaths[pool][fam] = pathSet
   640  				}
   641  			}
   642  
   643  			req.Equal(tt.expectedPoolAFPaths, runningPoolAFPaths)
   644  			req.Equal(tt.expectedRPs, podIPPoolReconciler.getMetadata(testBGPInstance).PoolRoutePolicies)
   645  		})
   646  	}
   647  }