github.com/cilium/cilium@v1.16.2/pkg/maps/ctmap/per_cluster_ctmap_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ctmap
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/cilium/ebpf/rlimit"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/cilium/cilium/pkg/bpf"
    13  	cmtypes "github.com/cilium/cilium/pkg/clustermesh/types"
    14  	"github.com/cilium/cilium/pkg/testutils"
    15  )
    16  
    17  func setup(tb testing.TB) {
    18  	testutils.PrivilegedTest(tb)
    19  
    20  	bpf.CheckOrMountFS("")
    21  	require.NoError(tb, rlimit.RemoveMemlock(), "Failed to set memlock rlimit")
    22  
    23  	// Override the map names to avoid clashing with the real ones.
    24  	ClusterOuterMapNameTestOverride("test")
    25  }
    26  
    27  func BenchmarkPerClusterCTMapUpdate(b *testing.B) {
    28  	b.StopTimer()
    29  	setup(b)
    30  
    31  	om := newPerClusterCTMap(mapTypeIPv4TCPGlobal)
    32  	require.NotNil(b, om, "Failed to initialize map")
    33  
    34  	require.NoError(b, om.OpenOrCreate(), "Failed to create outer map")
    35  	b.Cleanup(func() {
    36  		require.NoError(b, om.Close())
    37  		require.NoError(b, CleanupPerClusterCTMaps(true, true), "Failed to cleanup maps")
    38  	})
    39  
    40  	b.StartTimer()
    41  
    42  	for i := 0; i < b.N; i++ {
    43  		require.NoError(b, om.createClusterCTMap(1), "Failed to create map")
    44  	}
    45  
    46  	b.StopTimer()
    47  }
    48  
    49  func BenchmarkPerClusterCTMapLookup(b *testing.B) {
    50  	b.StopTimer()
    51  	setup(b)
    52  
    53  	om := newPerClusterCTMap(mapTypeIPv4TCPGlobal)
    54  	require.NotNil(b, om, "Failed to initialize map")
    55  
    56  	require.NoError(b, om.OpenOrCreate(), "Failed to create outer map")
    57  	b.Cleanup(func() {
    58  		require.NoError(b, om.Close())
    59  		require.NoError(b, CleanupPerClusterCTMaps(true, true), "Failed to cleanup maps")
    60  	})
    61  
    62  	require.NoError(b, om.createClusterCTMap(1), "Failed to create map")
    63  
    64  	b.StartTimer()
    65  
    66  	key := &PerClusterCTMapKey{1}
    67  	for i := 0; i < b.N; i++ {
    68  		_, err := om.Lookup(key)
    69  		require.NoError(b, err, "Failed to lookup element")
    70  	}
    71  
    72  	b.StopTimer()
    73  }
    74  
    75  func TestPerClusterCTMaps(t *testing.T) {
    76  	setup(t)
    77  
    78  	maps := NewPerClusterCTMaps(true, true)
    79  	for _, om := range []*PerClusterCTMap{maps.tcp4, maps.any4, maps.tcp6, maps.any6} {
    80  		require.NotNil(t, om, "Failed to initialize maps")
    81  	}
    82  
    83  	require.NoError(t, maps.OpenOrCreate(), "Failed to create outer maps")
    84  	for _, om := range []*PerClusterCTMap{maps.tcp4, maps.any4, maps.tcp6, maps.any6} {
    85  		require.FileExists(t, bpf.MapPath(om.Map.Name()), "Failed to create outer maps")
    86  	}
    87  
    88  	t.Cleanup(func() {
    89  		require.NoError(t, maps.Close())
    90  		require.NoError(t, CleanupPerClusterCTMaps(true, true), "Failed to cleanup maps")
    91  	})
    92  
    93  	// ClusterID 0 should never be used
    94  	require.Error(t, maps.CreateClusterCTMaps(0), "ClusterID 0 should never be used")
    95  	require.Error(t, maps.DeleteClusterCTMaps(0), "ClusterID 0 should never be used")
    96  	_, err := GetClusterCTMaps(0, true, true)
    97  	require.Error(t, err, "ClusterID 0 should never be used")
    98  
    99  	// ClusterID beyond the ClusterIDMax should never be used
   100  	require.Error(t, maps.CreateClusterCTMaps(cmtypes.ClusterIDMax+1), "ClusterID beyond the ClusterIDMax should never be used")
   101  	require.Error(t, maps.DeleteClusterCTMaps(cmtypes.ClusterIDMax+1), "ClusterID beyond the ClusterIDMax should never be used")
   102  	_, err = GetClusterCTMaps(cmtypes.ClusterIDMax+1, true, true)
   103  	require.Error(t, err, "ClusterID beyond the ClusterIDMax should never be used")
   104  
   105  	// Basic update
   106  	require.NoError(t, maps.CreateClusterCTMaps(1), "Failed to create maps")
   107  	require.NoError(t, maps.CreateClusterCTMaps(cmtypes.ClusterIDMax), "Failed to create maps")
   108  
   109  	for _, id := range []uint32{1, cmtypes.ClusterIDMax} {
   110  		for _, om := range []*PerClusterCTMap{maps.tcp4, maps.any4, maps.tcp6, maps.any6} {
   111  			// After update, the outer map should be updated with the inner map
   112  			value, err := om.Lookup(&PerClusterCTMapKey{id})
   113  			require.NoError(t, err, "Outer map not updated correctly (id=%v, map=%v)", id, om.Name())
   114  			require.NotZero(t, value, "Outer map not updated correctly (id=%v, map=%v)", id, om.Name())
   115  
   116  			// After update, the inner map should exist
   117  			require.FileExists(t, bpf.MapPath(om.newInnerMap(id).Map.Name()), "Inner map not correctly present (id=%v, map=%v)", id, om.Name())
   118  		}
   119  
   120  		// After update, it should be possible to get and open the inner map
   121  		ims, err := GetClusterCTMaps(id, true, true)
   122  		require.Len(t, ims, 4, "Retrieved an incorrect number of inner maps")
   123  		for _, im := range ims {
   124  			require.NotNil(t, im, "Failed to get inner map (id=%v, map=%v)", id, im.Name())
   125  			require.NoError(t, err, "Failed to get inner map (id=%v, map=%v)", id, im.Name())
   126  			require.NoError(t, im.Open(), "Failed to open inner map (id=%v, map=%v)", im.Name())
   127  			im.Close()
   128  		}
   129  	}
   130  
   131  	// An update for an already existing entry should succeed
   132  	require.NoError(t, maps.CreateClusterCTMaps(cmtypes.ClusterIDMax), "Failed to create maps")
   133  
   134  	// Basic get all
   135  	ims := maps.GetAllClusterCTMaps()
   136  	require.Len(t, ims, 8, "Retrieved an unexpected number of maps")
   137  
   138  	// Basic delete
   139  	require.NoError(t, maps.DeleteClusterCTMaps(1), "Failed to delete maps")
   140  	require.NoError(t, maps.DeleteClusterCTMaps(cmtypes.ClusterIDMax), "Failed to delete maps")
   141  
   142  	for _, id := range []uint32{1, cmtypes.ClusterIDMax} {
   143  		for _, om := range []*PerClusterCTMap{maps.tcp4, maps.any4, maps.tcp6, maps.any6} {
   144  			// After delete, the outer map shouldn't contain the entry
   145  			_, err := om.Lookup(&PerClusterCTMapKey{id})
   146  			require.Error(t, err, "Outer map not updated correctly (id=%v, map=%v)", id, om.Name())
   147  
   148  			// After delete, the inner map should not exist
   149  			require.NoFileExists(t, bpf.MapPath(om.newInnerMap(id).Map.Name()), "Inner map not correctly deleted (id=%v, map=%v)", id, om.Name())
   150  		}
   151  
   152  		// After delete, it should be no longer be possible to open the inner map
   153  		ims, err := GetClusterCTMaps(id, true, true)
   154  		require.Len(t, ims, 4, "Retrieved an incorrect number of inner maps")
   155  		for _, im := range ims {
   156  			require.NotNil(t, im, "Failed to get inner map (id=%v, map=%v)", id, im.Name())
   157  			require.NoError(t, err, "Failed to get inner map (id=%v, map=%v)", id, im.Name())
   158  			require.Error(t, im.Open(), "Should have failed to open inner map (id=%v, map=%v)", id, im.Name())
   159  		}
   160  	}
   161  
   162  	// A deletion for an already deleted entry should succeed
   163  	require.NoError(t, maps.DeleteClusterCTMaps(cmtypes.ClusterIDMax), "Failed to delete maps")
   164  }
   165  
   166  func TestPerClusterCTMapsCleanup(t *testing.T) {
   167  	setup(t)
   168  
   169  	tests := []struct {
   170  		name            string
   171  		ipv4, ipv6      bool
   172  		present, absent []mapType
   173  	}{
   174  		{
   175  			name:    "IPv4",
   176  			ipv4:    true,
   177  			present: []mapType{mapTypeIPv6TCPGlobal, mapTypeIPv6AnyLocal},
   178  			absent:  []mapType{mapTypeIPv4TCPGlobal, mapTypeIPv4AnyLocal},
   179  		},
   180  		{
   181  			name:    "IPv6",
   182  			ipv6:    true,
   183  			present: []mapType{mapTypeIPv4TCPGlobal, mapTypeIPv4AnyLocal},
   184  			absent:  []mapType{mapTypeIPv6TCPGlobal, mapTypeIPv6AnyLocal},
   185  		},
   186  		{
   187  			name:   "dual",
   188  			ipv4:   true,
   189  			ipv6:   true,
   190  			absent: []mapType{mapTypeIPv4TCPGlobal, mapTypeIPv4AnyLocal, mapTypeIPv6TCPGlobal, mapTypeIPv6AnyLocal},
   191  		},
   192  	}
   193  
   194  	for _, tt := range tests {
   195  		t.Run(tt.name, func(t *testing.T) {
   196  			// Pick up edge and middle values since filling all slots consumes too much memory.
   197  			ids := []uint32{1, 128, cmtypes.ClusterIDMax}
   198  			gm := NewPerClusterCTMaps(true, true)
   199  
   200  			require.NoError(t, gm.OpenOrCreate(), "Failed to create outer maps")
   201  			t.Cleanup(func() {
   202  				require.NoError(t, gm.Close())
   203  				// This also ensures that the cleanup succeeds even if the outer maps don't exist
   204  				require.NoError(t, CleanupPerClusterCTMaps(true, true), "Failed to cleanup maps")
   205  			})
   206  
   207  			for _, id := range ids {
   208  				require.NoError(t, gm.CreateClusterCTMaps(id), "Failed to create maps (id=%v)", id)
   209  			}
   210  
   211  			require.NoError(t, CleanupPerClusterCTMaps(tt.ipv4, tt.ipv6), "Failed to cleanup maps")
   212  
   213  			for _, typ := range tt.present {
   214  				for _, id := range ids {
   215  					require.FileExists(t, bpf.MapPath(ClusterInnerMapName(typ, id)), "Inner map should not have been deleted (id=%v, type=%v)", id, typ.name())
   216  				}
   217  				require.FileExists(t, bpf.MapPath(ClusterOuterMapName(typ)), "Outer map should not have been deleted (type=%v)", typ.name())
   218  			}
   219  
   220  			for _, typ := range tt.absent {
   221  				for _, id := range ids {
   222  					require.NoFileExists(t, bpf.MapPath(ClusterInnerMapName(typ, id)), "Inner map should have been deleted (id=%v, type=%v)", id, typ.name())
   223  				}
   224  				require.NoFileExists(t, bpf.MapPath(ClusterOuterMapName(typ)), "Outer map should have been deleted (type=%v)", typ.name())
   225  			}
   226  		})
   227  	}
   228  }