github.com/cilium/cilium@v1.16.2/pkg/bgpv1/manager/reconciler/pod_cidr_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package reconciler
     5  
     6  import (
     7  	"context"
     8  	"net/netip"
     9  	"testing"
    10  
    11  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/utils/ptr"
    13  
    14  	"github.com/cilium/cilium/pkg/bgpv1/manager/instance"
    15  	"github.com/cilium/cilium/pkg/bgpv1/types"
    16  	ipamtypes "github.com/cilium/cilium/pkg/ipam/types"
    17  	v2api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    18  	v2alpha1api "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1"
    19  	"github.com/cilium/cilium/pkg/option"
    20  )
    21  
    22  func TestExportPodCIDRReconciler(t *testing.T) {
    23  	var table = []struct {
    24  		// name of the test case
    25  		name string
    26  		// whether ExportPodCIDR is enabled at start of test
    27  		enabled bool
    28  		// whether ExportPodCIDR should be enabled before reconciliation
    29  		shouldEnable bool
    30  		// the advertised PodCIDR blocks the test begins with, these are encoded
    31  		// into Golang structs for the convenience of passing directly to the
    32  		// ServerWithConfig.AdvertisePath() method.
    33  		advertised []netip.Prefix
    34  		// the updated PodCIDR blocks to reconcile.
    35  		updated []string
    36  		// error nil or not
    37  		err error
    38  	}{
    39  		{
    40  			name:         "disable",
    41  			enabled:      true,
    42  			shouldEnable: false,
    43  			advertised: []netip.Prefix{
    44  				netip.MustParsePrefix("192.168.0.0/24"),
    45  			},
    46  		},
    47  		{
    48  			name:         "enable",
    49  			enabled:      false,
    50  			shouldEnable: true,
    51  			updated:      []string{"192.168.0.0/24"},
    52  		},
    53  		{
    54  			name:         "no change",
    55  			enabled:      true,
    56  			shouldEnable: true,
    57  			advertised: []netip.Prefix{
    58  				netip.MustParsePrefix("192.168.0.0/24"),
    59  			},
    60  			updated: []string{"192.168.0.0/24"},
    61  		},
    62  		{
    63  			name:         "additional network",
    64  			enabled:      true,
    65  			shouldEnable: true,
    66  			advertised: []netip.Prefix{
    67  				netip.MustParsePrefix("192.168.0.0/24"),
    68  			},
    69  			updated: []string{"192.168.0.0/24", "192.168.1.0/24"},
    70  		},
    71  		{
    72  			name:         "removal of both networks",
    73  			enabled:      true,
    74  			shouldEnable: true,
    75  			advertised: []netip.Prefix{
    76  				netip.MustParsePrefix("192.168.0.0/24"),
    77  				netip.MustParsePrefix("192.168.1.0/24"),
    78  			},
    79  			updated: []string{},
    80  		},
    81  	}
    82  
    83  	// Dummy daemon config and logger
    84  	daemonConfig := &option.DaemonConfig{IPAM: "Kubernetes"}
    85  
    86  	for _, tt := range table {
    87  		t.Run(tt.name, func(t *testing.T) {
    88  			// setup our test server, create a BgpServer, advertise the tt.advertised
    89  			// networks, and store each returned Advertisement in testSC.PodCIDRAnnouncements
    90  			srvParams := types.ServerParameters{
    91  				Global: types.BGPGlobal{
    92  					ASN:        64125,
    93  					RouterID:   "127.0.0.1",
    94  					ListenPort: -1,
    95  				},
    96  			}
    97  			oldc := &v2alpha1api.CiliumBGPVirtualRouter{
    98  				LocalASN:      64125,
    99  				ExportPodCIDR: ptr.To[bool](tt.enabled),
   100  				Neighbors:     []v2alpha1api.CiliumBGPNeighbor{},
   101  			}
   102  			testSC, err := instance.NewServerWithConfig(context.Background(), log, srvParams)
   103  			if err != nil {
   104  				t.Fatalf("failed to create test bgp server: %v", err)
   105  			}
   106  			testSC.Config = oldc
   107  			reconciler := NewExportPodCIDRReconciler(daemonConfig).Reconciler.(*ExportPodCIDRReconciler)
   108  			podCIDRAnnouncements := reconciler.getMetadata(testSC)
   109  			for _, cidr := range tt.advertised {
   110  				advrtResp, err := testSC.Server.AdvertisePath(context.Background(), types.PathRequest{
   111  					Path: types.NewPathForPrefix(cidr),
   112  				})
   113  				if err != nil {
   114  					t.Fatalf("failed to advertise initial pod cidr routes: %v", err)
   115  				}
   116  				podCIDRAnnouncements = append(podCIDRAnnouncements, advrtResp.Path)
   117  			}
   118  			reconciler.storeMetadata(testSC, podCIDRAnnouncements)
   119  
   120  			newc := &v2alpha1api.CiliumBGPVirtualRouter{
   121  				LocalASN:      64125,
   122  				ExportPodCIDR: ptr.To[bool](tt.shouldEnable),
   123  				Neighbors:     []v2alpha1api.CiliumBGPNeighbor{},
   124  			}
   125  
   126  			exportPodCIDRReconciler := NewExportPodCIDRReconciler(daemonConfig).Reconciler
   127  			params := ReconcileParams{
   128  				CurrentServer: testSC,
   129  				DesiredConfig: newc,
   130  				CiliumNode: &v2api.CiliumNode{
   131  					ObjectMeta: meta_v1.ObjectMeta{
   132  						Name: "Test Node",
   133  					},
   134  					Spec: v2api.NodeSpec{
   135  						IPAM: ipamtypes.IPAMSpec{
   136  							PodCIDRs: tt.updated,
   137  						},
   138  					},
   139  				},
   140  			}
   141  
   142  			// Run the reconciler twice to ensure idempotency. This
   143  			// simulates the retrying behavior of the controller.
   144  			for i := 0; i < 2; i++ {
   145  				t.Run(tt.name, func(t *testing.T) {
   146  					err = exportPodCIDRReconciler.Reconcile(context.Background(), params)
   147  					if err != nil {
   148  						t.Fatalf("failed to reconcile new pod cidr advertisements: %v", err)
   149  					}
   150  				})
   151  			}
   152  			podCIDRAnnouncements = reconciler.getMetadata(testSC)
   153  
   154  			// if we disable exports of pod cidr ensure no advertisements are
   155  			// still present.
   156  			if tt.shouldEnable == false {
   157  				if len(podCIDRAnnouncements) > 0 {
   158  					t.Fatal("disabled export but advertisements till present")
   159  				}
   160  			}
   161  
   162  			log.Printf("%+v %+v", podCIDRAnnouncements, tt.updated)
   163  
   164  			// ensure we see tt.updated in testSC.PodCIDRAnnoucements
   165  			for _, cidr := range tt.updated {
   166  				prefix := netip.MustParsePrefix(cidr)
   167  				var seen bool
   168  				for _, advrt := range podCIDRAnnouncements {
   169  					if advrt.NLRI.String() == prefix.String() {
   170  						seen = true
   171  					}
   172  				}
   173  				if !seen {
   174  					t.Fatalf("failed to advertise %v", cidr)
   175  				}
   176  			}
   177  
   178  			// ensure testSC.PodCIDRAnnouncements does not contain advertisements
   179  			// not in tt.updated
   180  			for _, advrt := range podCIDRAnnouncements {
   181  				var seen bool
   182  				for _, cidr := range tt.updated {
   183  					if advrt.NLRI.String() == cidr {
   184  						seen = true
   185  					}
   186  				}
   187  				if !seen {
   188  					t.Fatalf("unwanted advert %+v", advrt)
   189  				}
   190  			}
   191  
   192  		})
   193  	}
   194  }