github.com/cilium/cilium@v1.16.2/pkg/bpf/map_linux_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package bpf
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"os"
    11  	"reflect"
    12  	"strconv"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/cilium/ebpf"
    19  	"github.com/cilium/ebpf/rlimit"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  
    23  	"github.com/cilium/cilium/pkg/option"
    24  	"github.com/cilium/cilium/pkg/testutils"
    25  )
    26  
    27  // Configure a generous timeout to prevent flakes when running in a noisy CI environment.
    28  const (
    29  	tick    = 100 * time.Millisecond
    30  	timeout = 10 * time.Second
    31  )
    32  
    33  type TestKey struct {
    34  	Key uint32
    35  }
    36  type TestLPMKey struct {
    37  	PrefixLen uint32
    38  	Key       uint32
    39  }
    40  type TestValue struct {
    41  	Value uint32
    42  }
    43  
    44  func (k *TestKey) String() string { return fmt.Sprintf("key=%d", k.Key) }
    45  func (k *TestKey) New() MapKey    { return &TestKey{} }
    46  
    47  func (k *TestLPMKey) String() string { return fmt.Sprintf("len=%d, key=%d", k.PrefixLen, k.Key) }
    48  func (k *TestLPMKey) New() MapKey    { return &TestLPMKey{} }
    49  
    50  func (v *TestValue) String() string { return fmt.Sprintf("value=%d", v.Value) }
    51  func (v *TestValue) New() MapValue  { return &TestValue{} }
    52  
    53  func setup(tb testing.TB) *Map {
    54  	testutils.PrivilegedTest(tb)
    55  
    56  	CheckOrMountFS("")
    57  
    58  	err := rlimit.RemoveMemlock()
    59  	require.NoError(tb, err)
    60  
    61  	testMap := NewMap("cilium_test",
    62  		ebpf.Hash,
    63  		&TestKey{},
    64  		&TestValue{},
    65  		maxEntries,
    66  		BPF_F_NO_PREALLOC,
    67  	).WithCache()
    68  
    69  	err = testMap.OpenOrCreate()
    70  	require.NoError(tb, err, "Failed to create map")
    71  
    72  	tb.Cleanup(func() {
    73  		require.NoError(tb, testMap.Close())
    74  	})
    75  
    76  	return testMap
    77  }
    78  
    79  var (
    80  	maxEntries = 16
    81  )
    82  
    83  func mapsEqual(a, b *Map) bool {
    84  	return a.name == b.name &&
    85  		reflect.DeepEqual(a.spec, b.spec)
    86  }
    87  
    88  func TestOpen(t *testing.T) {
    89  	setup(t)
    90  
    91  	// Ensure that os.IsNotExist() can be used with Map.Open()
    92  	noSuchMap := NewMap("cilium_test_no_exist",
    93  		ebpf.Hash, &TestKey{}, &TestValue{}, maxEntries, 0)
    94  	err := noSuchMap.Open()
    95  	require.True(t, errors.Is(err, os.ErrNotExist))
    96  
    97  	// existingMap is the same as testMap. Opening should succeed.
    98  	existingMap := NewMap("cilium_test",
    99  		ebpf.Hash,
   100  		&TestKey{},
   101  		&TestValue{},
   102  		maxEntries,
   103  		BPF_F_NO_PREALLOC).WithCache()
   104  	defer func() {
   105  		err = existingMap.Close()
   106  		require.NoError(t, err)
   107  	}()
   108  
   109  	err = existingMap.Open()
   110  	require.NoError(t, err)
   111  	err = existingMap.Open()
   112  	require.NoError(t, err)
   113  }
   114  
   115  func TestOpenMap(t *testing.T) {
   116  	testMap := setup(t)
   117  
   118  	openedMap, err := OpenMap("cilium_test_no_exist", &TestKey{}, &TestValue{})
   119  	require.Error(t, err)
   120  	require.Nil(t, openedMap)
   121  
   122  	openedMap, err = OpenMap(MapPath("cilium_test"), &TestKey{}, &TestValue{})
   123  	require.NoError(t, err)
   124  	require.True(t, mapsEqual(openedMap, testMap))
   125  }
   126  
   127  func TestOpenOrCreate(t *testing.T) {
   128  	setup(t)
   129  
   130  	// existingMap is the same as testMap. OpenOrCreate should skip recreation.
   131  	existingMap := NewMap("cilium_test",
   132  		ebpf.Hash,
   133  		&TestKey{},
   134  		&TestValue{},
   135  		maxEntries,
   136  		BPF_F_NO_PREALLOC).WithCache()
   137  	err := existingMap.OpenOrCreate()
   138  	require.NoError(t, err)
   139  
   140  	// preallocMap unsets BPF_F_NO_PREALLOC. OpenOrCreate should recreate map.
   141  	EnableMapPreAllocation() // prealloc on/off is controllable in HASH map case.
   142  	preallocMap := NewMap("cilium_test",
   143  		ebpf.Hash,
   144  		&TestKey{},
   145  		&TestValue{},
   146  		maxEntries,
   147  		0).WithCache()
   148  	err = preallocMap.OpenOrCreate()
   149  	defer preallocMap.Close()
   150  	require.NoError(t, err)
   151  	DisableMapPreAllocation()
   152  
   153  	// preallocMap is already open. OpenOrCreate does nothing.
   154  	err = preallocMap.OpenOrCreate()
   155  	require.NoError(t, err)
   156  }
   157  
   158  func TestRecreateMap(t *testing.T) {
   159  	testMap := setup(t)
   160  
   161  	parallelMap := NewMap("cilium_test",
   162  		ebpf.Hash,
   163  		&TestKey{},
   164  		&TestValue{},
   165  		maxEntries,
   166  		BPF_F_NO_PREALLOC).WithCache()
   167  	err := parallelMap.Recreate()
   168  	defer parallelMap.Close()
   169  	require.NoError(t, err)
   170  
   171  	err = parallelMap.Recreate()
   172  	require.Error(t, err)
   173  
   174  	// Check OpenMap warning section
   175  	require.True(t, mapsEqual(parallelMap, testMap))
   176  
   177  	key1 := &TestKey{Key: 101}
   178  	value1 := &TestValue{Value: 201}
   179  	key2 := &TestKey{Key: 102}
   180  	value2 := &TestValue{Value: 202}
   181  
   182  	err = testMap.Update(key1, value1)
   183  	require.NoError(t, err)
   184  	err = parallelMap.Update(key2, value2)
   185  	require.NoError(t, err)
   186  
   187  	value, err := testMap.Lookup(key1)
   188  	require.NoError(t, err)
   189  	require.EqualValues(t, value, value1)
   190  	value, err = testMap.Lookup(key2)
   191  	require.Error(t, err)
   192  	require.Nil(t, value)
   193  
   194  	value, err = parallelMap.Lookup(key1)
   195  	require.Error(t, err)
   196  	require.Nil(t, value)
   197  	value, err = parallelMap.Lookup(key2)
   198  	require.NoError(t, err)
   199  	require.EqualValues(t, value, value2)
   200  }
   201  
   202  func TestBasicManipulation(t *testing.T) {
   203  	setup(t)
   204  	// existingMap is the same as testMap. Opening should succeed.
   205  	existingMap := NewMap("cilium_test",
   206  		ebpf.Hash,
   207  		&TestKey{},
   208  		&TestValue{},
   209  		maxEntries,
   210  		BPF_F_NO_PREALLOC).
   211  		WithCache().
   212  		WithEvents(option.BPFEventBufferConfig{Enabled: true, MaxSize: 10})
   213  
   214  	err := existingMap.Open()
   215  	defer existingMap.Close()
   216  	require.NoError(t, err)
   217  
   218  	key1 := &TestKey{Key: 103}
   219  	value1 := &TestValue{Value: 203}
   220  	key2 := &TestKey{Key: 104}
   221  	value2 := &TestValue{Value: 204}
   222  
   223  	dumpEvents := func() []*Event {
   224  		es := []*Event{}
   225  		existingMap.DumpAndSubscribe(func(e *Event) {
   226  			es = append(es, e)
   227  		}, false)
   228  		return es
   229  	}
   230  	event := func(i int) *Event {
   231  		es := dumpEvents()
   232  		if i >= len(es) {
   233  			return nil
   234  		}
   235  		return dumpEvents()[i]
   236  	}
   237  	assertEvent := func(i int, key, value, desiredAction, action string) {
   238  		e := event(i)
   239  		if e.cacheEntry.Key != nil {
   240  			require.Equal(t, key, e.cacheEntry.Key.String())
   241  		}
   242  		require.Equal(t, e.GetValue(), value)
   243  		require.Equal(t, e.cacheEntry.DesiredAction.String(), desiredAction)
   244  		require.Equal(t, e.GetAction(), action)
   245  	}
   246  
   247  	// event buffer should be empty
   248  	require.Equal(t, existingMap.events.buffer.Size(), 0)
   249  
   250  	err = existingMap.Update(key1, value1)
   251  	require.NoError(t, err)
   252  
   253  	// Check events buffer
   254  	require.Len(t, dumpEvents(), 1)
   255  	require.Equal(t, "key=103", event(0).cacheEntry.Key.String())
   256  	require.Equal(t, "value=203", event(0).cacheEntry.Value.String())
   257  
   258  	// key    val
   259  	// 103    203
   260  	value, err := existingMap.Lookup(key1)
   261  	require.NoError(t, err)
   262  	require.EqualValues(t, value, value1)
   263  	value, err = existingMap.Lookup(key2)
   264  	require.Error(t, err)
   265  	require.Nil(t, value)
   266  
   267  	// Check events buffer, ensure it doesn't change.
   268  	require.Len(t, dumpEvents(), 1)
   269  	require.Equal(t, "key=103", event(0).cacheEntry.Key.String())
   270  	require.Equal(t, "value=203", event(0).cacheEntry.Value.String())
   271  
   272  	err = existingMap.Update(key1, value2)
   273  	require.NoError(t, err)
   274  	// key    val
   275  	// 103    204
   276  	value, err = existingMap.Lookup(key1)
   277  	require.NoError(t, err)
   278  	require.EqualValues(t, value, value2)
   279  
   280  	// Check events buffer after second Update
   281  	require.Len(t, dumpEvents(), 2)
   282  	assertEvent(0, "key=103", "value=203", "sync", "update")
   283  	require.Equal(t, "key=103", event(0).cacheEntry.Key.String())
   284  	require.Equal(t, "value=203", event(0).cacheEntry.Value.String())
   285  	require.Equal(t, "sync", event(0).cacheEntry.DesiredAction.String())
   286  	require.Equal(t, "key=103", event(1).cacheEntry.Key.String()) // we used key1 again
   287  	require.Equal(t, "value=204", event(1).cacheEntry.Value.String())
   288  	require.Equal(t, "sync", event(1).cacheEntry.DesiredAction.String())
   289  
   290  	err = existingMap.Update(key2, value2)
   291  	require.NoError(t, err)
   292  	// key    val
   293  	// 103    204
   294  	// 104    204
   295  	value, err = existingMap.Lookup(key1)
   296  	require.NoError(t, err)
   297  	require.EqualValues(t, value, value2)
   298  	value, err = existingMap.Lookup(key2)
   299  	require.NoError(t, err)
   300  	require.EqualValues(t, value, value2)
   301  
   302  	require.Len(t, dumpEvents(), 3)
   303  	assertEvent(0, "key=103", "value=203", "sync", "update")
   304  	assertEvent(1, "key=103", "value=204", "sync", "update")
   305  	assertEvent(2, "key=104", "value=204", "sync", "update")
   306  
   307  	err = existingMap.Delete(key1)
   308  	require.NoError(t, err)
   309  	// key    val
   310  	// 104    204
   311  	value, err = existingMap.Lookup(key1)
   312  	require.Error(t, err)
   313  	require.Nil(t, value)
   314  
   315  	err = existingMap.Delete(key1)
   316  	require.Error(t, err)
   317  
   318  	require.Len(t, dumpEvents(), 5)
   319  	assertEvent(0, "key=103", "value=203", "sync", "update")
   320  	assertEvent(1, "key=103", "value=204", "sync", "update")
   321  	assertEvent(2, "key=104", "value=204", "sync", "update")
   322  	assertEvent(3, "key=103", "<nil>", Delete.String(), "delete")
   323  	assertEvent(4, "key=103", "<nil>", Delete.String(), "delete")
   324  
   325  	require.NoError(t, event(3).GetLastError())
   326  	require.Error(t, event(4).GetLastError())
   327  
   328  	deleted, err := existingMap.SilentDelete(key1)
   329  	require.NoError(t, err)
   330  	require.False(t, deleted)
   331  
   332  	require.Len(t, dumpEvents(), 6)
   333  	assertEvent(5, "key=103", "<nil>", Delete.String(), "delete")
   334  	require.NoError(t, event(5).GetLastError())
   335  
   336  	err = existingMap.Update(key1, value1)
   337  	require.NoError(t, err)
   338  
   339  	require.Len(t, dumpEvents(), 7)
   340  	assertEvent(6, "key=103", "value=203", OK.String(), "update")
   341  
   342  	deleted, err = existingMap.SilentDelete(key1)
   343  	require.NoError(t, err)
   344  	require.True(t, deleted)
   345  
   346  	require.Len(t, dumpEvents(), 8)
   347  	assertEvent(7, "key=103", "<nil>", Delete.String(), "delete")
   348  
   349  	value, err = existingMap.Lookup(key1)
   350  	require.Error(t, err)
   351  	require.Nil(t, value)
   352  
   353  	err = existingMap.DeleteAll()
   354  	require.NoError(t, err)
   355  	value, err = existingMap.Lookup(key1)
   356  	require.Error(t, err)
   357  	require.Nil(t, value)
   358  	value, err = existingMap.Lookup(key2)
   359  	require.Error(t, err)
   360  	require.Nil(t, value)
   361  
   362  	require.Len(t, dumpEvents(), 9)
   363  	assertEvent(8, "key=104", "<nil>", "sync", "delete-all")
   364  
   365  	require.Equal(t, "key=103", event(0).cacheEntry.Key.String())
   366  	require.Equal(t, "value=203", event(0).cacheEntry.Value.String())
   367  
   368  	require.Equal(t, "key=103", event(1).cacheEntry.Key.String()) // we used key1 again
   369  
   370  	err = existingMap.Update(key2, value2)
   371  	require.NoError(t, err)
   372  	require.Len(t, dumpEvents(), 10)
   373  	assertEvent(9, "key=104", "value=204", OK.String(), "update")
   374  
   375  	key3 := &TestKey{Key: 999}
   376  	err = existingMap.Update(key3, value2)
   377  	require.NoError(t, err)
   378  	require.Len(t, dumpEvents(), 10) // full buffer
   379  	assertEvent(0, "key=103", "value=204", OK.String(), "update")
   380  	assertEvent(9, "key=999", "value=204", OK.String(), "update")
   381  
   382  	key4 := &TestKey{Key: 1000}
   383  	err = existingMap.Update(key4, value2)
   384  	require.NoError(t, err)
   385  	err = existingMap.DeleteAll()
   386  	require.NoError(t, err)
   387  	assertEvent(9, "<nil>", "<nil>", OK.String(), MapDeleteAll.String())
   388  
   389  	// cleanup
   390  	err = existingMap.DeleteAll()
   391  	require.NoError(t, err)
   392  }
   393  
   394  func TestSubscribe(t *testing.T) {
   395  	setup(t)
   396  
   397  	existingMap := NewMap("cilium_test",
   398  		ebpf.Hash,
   399  		&TestKey{},
   400  		&TestValue{},
   401  		maxEntries,
   402  		BPF_F_NO_PREALLOC).
   403  		WithCache().
   404  		WithEvents(option.BPFEventBufferConfig{Enabled: true, MaxSize: 10})
   405  
   406  	subHandle, err := existingMap.DumpAndSubscribe(nil, true)
   407  	require.NoError(t, err)
   408  
   409  	collect := 0
   410  	done := make(chan struct{})
   411  	go func(collect *int) {
   412  		defer subHandle.Close()
   413  		for range subHandle.C() {
   414  			*collect++
   415  		}
   416  		close(done)
   417  	}(&collect)
   418  
   419  	key1 := &TestKey{Key: 103}
   420  	value1 := &TestValue{Value: 203}
   421  	err = existingMap.Update(key1, value1)
   422  	require.NoError(t, err)
   423  	err = existingMap.Update(key1, value1)
   424  	require.NoError(t, err)
   425  	err = existingMap.Delete(key1)
   426  	require.NoError(t, err)
   427  
   428  	subHandle.Close()
   429  	<-done
   430  	require.Equal(t, collect, 3)
   431  
   432  	// cleanup
   433  	err = existingMap.DeleteAll()
   434  	existingMap.events = nil
   435  	require.NoError(t, err)
   436  }
   437  
   438  func TestDump(t *testing.T) {
   439  	testMap := setup(t)
   440  
   441  	key1 := &TestKey{Key: 105}
   442  	value1 := &TestValue{Value: 205}
   443  	key2 := &TestKey{Key: 106}
   444  	value2 := &TestValue{Value: 206}
   445  
   446  	err := testMap.Update(key1, value1)
   447  	require.NoError(t, err)
   448  	err = testMap.Update(key2, value1)
   449  	require.NoError(t, err)
   450  	err = testMap.Update(key2, value2)
   451  	require.NoError(t, err)
   452  
   453  	dump1 := map[string][]string{}
   454  	testMap.Dump(dump1)
   455  	require.Equal(t, map[string][]string{
   456  		"key=105": {"value=205"},
   457  		"key=106": {"value=206"},
   458  	}, dump1)
   459  
   460  	dump2 := map[string][]string{}
   461  	customCb := func(key MapKey, value MapValue) {
   462  		dump2[key.String()] = append(dump2[key.String()], "custom-"+value.String())
   463  	}
   464  	testMap.DumpWithCallback(customCb)
   465  	require.Equal(t, map[string][]string{
   466  		"key=105": {"custom-value=205"},
   467  		"key=106": {"custom-value=206"},
   468  	}, dump2)
   469  
   470  	dump3 := map[string][]string{}
   471  	noSuchMap := NewMap("cilium_test_no_exist",
   472  		ebpf.Hash, &TestKey{}, &TestValue{}, maxEntries, 0)
   473  	err = noSuchMap.DumpIfExists(dump3)
   474  	require.NoError(t, err)
   475  	require.Len(t, dump3, 0)
   476  
   477  	dump2 = map[string][]string{}
   478  	err = noSuchMap.DumpWithCallbackIfExists(customCb)
   479  	require.NoError(t, err)
   480  	require.Len(t, dump2, 0)
   481  
   482  	// Validate that if the key is zero, it shows up in dump output.
   483  	keyZero := &TestKey{Key: 0}
   484  	valueZero := &TestValue{Value: 0}
   485  	err = testMap.Update(keyZero, valueZero)
   486  	require.NoError(t, err)
   487  
   488  	dump4 := map[string][]string{}
   489  	customCb = func(key MapKey, value MapValue) {
   490  		dump4[key.String()] = append(dump4[key.String()], "custom-"+value.String())
   491  	}
   492  	ds := NewDumpStats(testMap)
   493  	err = testMap.DumpReliablyWithCallback(customCb, ds)
   494  	require.NoError(t, err)
   495  	require.Equal(t, map[string][]string{
   496  		"key=0":   {"custom-value=0"},
   497  		"key=105": {"custom-value=205"},
   498  		"key=106": {"custom-value=206"},
   499  	}, dump4)
   500  
   501  	dump5 := map[string][]string{}
   502  	err = testMap.Dump(dump5)
   503  	require.NoError(t, err)
   504  	require.Equal(t, map[string][]string{
   505  		"key=0":   {"value=0"},
   506  		"key=105": {"value=205"},
   507  		"key=106": {"value=206"},
   508  	}, dump5)
   509  }
   510  
   511  // TestDumpReliablyWithCallbackOverlapping attempts to test that DumpReliablyWithCallback
   512  // will reliably iterate all keys that are known to be in a map, even if keys that are ahead
   513  // of the current iteration can be deleted or updated concurrently.
   514  // This test is not deterministic, it establishes a condition where we have keys that are known
   515  // to be in the map and other keys which are volatile.  The test passes if the dump can reliably
   516  // iterate all keys that are not volatile.
   517  func TestDumpReliablyWithCallbackOverlapping(t *testing.T) {
   518  	setup(t)
   519  
   520  	iterations := 10000
   521  	maxEntries := uint32(128)
   522  	m := NewMap("cilium_dump_test2",
   523  		ebpf.Hash,
   524  		&TestKey{},
   525  		&TestValue{},
   526  		int(maxEntries),
   527  		BPF_F_NO_PREALLOC).WithCache()
   528  	err := m.OpenOrCreate()
   529  	require.NoError(t, err)
   530  	defer func() {
   531  		path, _ := m.Path()
   532  		os.Remove(path)
   533  	}()
   534  	defer m.Close()
   535  
   536  	// Prepopulate the map.
   537  	for i := uint32(0); i < maxEntries; i++ {
   538  		err := m.Update(&TestKey{Key: i}, &TestValue{Value: i + 200})
   539  		require.NoError(t, err)
   540  	}
   541  
   542  	// used to block the update/delete goroutine so that both start at aprox the same time.
   543  	start := make(chan struct{})
   544  	ctx, cancel := context.WithCancel(context.Background())
   545  	defer cancel()
   546  	wg := sync.WaitGroup{}
   547  	wg.Add(1)
   548  	// This goroutine will continuously delete and reinsert even keys.
   549  	// Thus, when this is running in parallel with DumpReliablyWithCallback
   550  	// it is unclear whether any even key will be iterated.
   551  	go func() {
   552  		defer wg.Done()
   553  		<-start
   554  		for {
   555  			select {
   556  			case <-ctx.Done():
   557  				return
   558  			default:
   559  			}
   560  
   561  			for i := uint32(0); i < maxEntries; i += 2 {
   562  				m.Delete(&TestKey{Key: i})
   563  				err := m.Update(&TestKey{Key: i}, &TestValue{Value: i + 200})
   564  				require.NoError(t, err)
   565  			}
   566  		}
   567  	}()
   568  
   569  	// We expect that DumpReliablyWithCallback will iterate all odd key/value pairs
   570  	// even if the even keys are being deleted and reinserted.
   571  	expect := map[string]string{}
   572  	for i := uint32(0); i < maxEntries; i++ {
   573  		if i%2 != 0 {
   574  			expect[fmt.Sprintf("key=%d", i)] = fmt.Sprintf("value=%d", i+200)
   575  		}
   576  	}
   577  	close(start) // start testing.
   578  	for i := 0; i < iterations; i++ {
   579  		dump := map[string]string{}
   580  		ds := NewDumpStats(m)
   581  		err := m.DumpReliablyWithCallback(func(key MapKey, value MapValue) {
   582  			k := key.(*TestKey).Key
   583  			if k%2 != 0 {
   584  				k := key.(*TestKey).Key
   585  				ks := dump[fmt.Sprintf("key=%d", k)]
   586  				if _, ok := dump[ks]; ok {
   587  					t.FailNow()
   588  				}
   589  				dump[fmt.Sprintf("key=%d", key.(*TestKey).Key)] = fmt.Sprintf("value=%d", value.(*TestValue).Value)
   590  			}
   591  		}, ds)
   592  		if err == nil {
   593  			require.Equal(t, expect, dump)
   594  		} else {
   595  			require.Equal(t, ErrMaxLookup, err)
   596  		}
   597  	}
   598  	cancel()
   599  	wg.Wait()
   600  }
   601  
   602  // TestDumpReliablyWithCallback tests that DumpReliablyWithCallback by concurrently
   603  // upserting/removing keys in range [0, 4) in the map and then continuously dumping
   604  // the map.
   605  // The test validates that all keys that are not being removed/added are contained in the dump.
   606  func TestDumpReliablyWithCallback(t *testing.T) {
   607  	setup(t)
   608  
   609  	maxEntries := uint32(256)
   610  	m := NewMap("cilium_dump_test",
   611  		ebpf.Hash,
   612  		&TestKey{},
   613  		&TestValue{},
   614  		int(maxEntries),
   615  		BPF_F_NO_PREALLOC).WithCache()
   616  	err := m.OpenOrCreate()
   617  	require.NoError(t, err)
   618  	defer func() {
   619  		path, _ := m.Path()
   620  		os.Remove(path)
   621  	}()
   622  	defer m.Close()
   623  
   624  	for i := uint32(4); i < maxEntries; i++ {
   625  		err := m.Update(&TestKey{Key: i}, &TestValue{Value: i + 100})
   626  		require.NoError(t, err) // we want to run the deferred calls
   627  	}
   628  	// start a goroutine that continuously updates the map
   629  	started := make(chan struct{}, 1)
   630  	done := make(chan struct{}, 1)
   631  	var wg sync.WaitGroup
   632  	wg.Add(1)
   633  	go func() {
   634  		defer wg.Done()
   635  		started <- struct{}{}
   636  		for {
   637  			for i := uint32(0); i < 4; i++ {
   638  				if i < 3 {
   639  					err := m.Update(&TestKey{Key: i}, &TestValue{Value: i + 100})
   640  					// avoid assert to ensure we call wg.Done
   641  					require.NoError(t, err)
   642  				}
   643  				if i > 0 {
   644  					err := m.Delete(&TestKey{Key: i - 1})
   645  					// avoid assert to ensure we call wg.Done
   646  					require.NoError(t, err)
   647  				}
   648  			}
   649  			select {
   650  			case <-done:
   651  				return
   652  			default:
   653  			}
   654  		}
   655  	}()
   656  	<-started // wait until the routine has started to start the actual tests
   657  	wg.Add(1)
   658  	go func() {
   659  		defer wg.Done()
   660  		expect := map[string]string{}
   661  		for i := uint32(4); i < maxEntries; i++ {
   662  			expect[fmt.Sprintf("key=%d", i)] = fmt.Sprintf("custom-value=%d", i+100)
   663  		}
   664  		for i := 0; i < 100; i++ {
   665  			dump := map[string]string{}
   666  			customCb := func(key MapKey, value MapValue) {
   667  				k, err := strconv.ParseUint(strings.TrimPrefix(key.String(), "key="), 10, 32)
   668  				require.NoError(t, err)
   669  				if uint32(k) >= 4 {
   670  					dump[key.String()] = "custom-" + value.String()
   671  				}
   672  			}
   673  			ds := NewDumpStats(m)
   674  			if i == 0 {
   675  				// artificially trigger MaxLookupError as max lookup is based
   676  				// on ds.MaxEntries
   677  				ds.MaxEntries = 1
   678  			}
   679  			if err := m.DumpReliablyWithCallback(customCb, ds); err != nil {
   680  				// avoid Assert to ensure the done signal is sent
   681  				require.Equal(t, ErrMaxLookup, err)
   682  			} else {
   683  				// avoid Assert to ensure the done signal is sent
   684  				require.Equal(t, expect, dump)
   685  			}
   686  		}
   687  		done <- struct{}{}
   688  	}()
   689  	wg.Wait()
   690  }
   691  
   692  func TestDeleteAll(t *testing.T) {
   693  	testMap := setup(t)
   694  
   695  	key1 := &TestKey{Key: 105}
   696  	value1 := &TestValue{Value: 205}
   697  	key2 := &TestKey{Key: 106}
   698  	value2 := &TestValue{Value: 206}
   699  
   700  	err := testMap.Update(key1, value1)
   701  	require.NoError(t, err)
   702  	err = testMap.Update(key2, value1)
   703  	require.NoError(t, err)
   704  	err = testMap.Update(key2, value2)
   705  	require.NoError(t, err)
   706  
   707  	keyZero := &TestKey{Key: 0}
   708  	valueZero := &TestValue{Value: 0}
   709  	err = testMap.Update(keyZero, valueZero)
   710  	require.NoError(t, err)
   711  
   712  	dump1 := map[string][]string{}
   713  	err = testMap.Dump(dump1)
   714  	require.NoError(t, err)
   715  	require.Equal(t, map[string][]string{
   716  		"key=0":   {"value=0"},
   717  		"key=105": {"value=205"},
   718  		"key=106": {"value=206"},
   719  	}, dump1)
   720  
   721  	err = testMap.DeleteAll()
   722  	require.NoError(t, err)
   723  
   724  	dump2 := map[string][]string{}
   725  	err = testMap.Dump(dump2)
   726  	require.NoError(t, err)
   727  }
   728  
   729  func TestGetModel(t *testing.T) {
   730  	testMap := setup(t)
   731  
   732  	model := testMap.GetModel()
   733  	require.NotNil(t, model)
   734  }
   735  
   736  func TestCheckAndUpgrade(t *testing.T) {
   737  	setup(t)
   738  
   739  	// CheckAndUpgrade removes map file if upgrade is needed
   740  	// so we setup and use another map.
   741  	upgradeMap := NewMap("cilium_test_upgrade",
   742  		ebpf.Hash,
   743  		&TestKey{},
   744  		&TestValue{},
   745  		maxEntries,
   746  		BPF_F_NO_PREALLOC).WithCache()
   747  	err := upgradeMap.OpenOrCreate()
   748  	require.NoError(t, err)
   749  	defer func() {
   750  		_ = upgradeMap.Unpin()
   751  		upgradeMap.Close()
   752  	}()
   753  
   754  	// Exactly the same MapInfo so it won't be upgraded.
   755  	upgrade := upgradeMap.CheckAndUpgrade(upgradeMap)
   756  	require.False(t, upgrade)
   757  
   758  	// preallocMap unsets BPF_F_NO_PREALLOC so upgrade is needed.
   759  	EnableMapPreAllocation()
   760  	preallocMap := NewMap("cilium_test_upgrade",
   761  		ebpf.Hash,
   762  		&TestKey{},
   763  		&TestValue{},
   764  		maxEntries,
   765  		0).WithCache()
   766  	upgrade = upgradeMap.CheckAndUpgrade(preallocMap)
   767  	require.True(t, upgrade)
   768  	DisableMapPreAllocation()
   769  }
   770  
   771  func TestUnpin(t *testing.T) {
   772  	setup(t)
   773  
   774  	var exist bool
   775  	unpinMap := NewMap("cilium_test_unpin",
   776  		ebpf.Hash,
   777  		&TestKey{},
   778  		&TestValue{},
   779  		maxEntries,
   780  		BPF_F_NO_PREALLOC).WithCache()
   781  	err := unpinMap.OpenOrCreate()
   782  	require.NoError(t, err)
   783  	exist, err = unpinMap.exist()
   784  	require.NoError(t, err)
   785  	require.True(t, exist)
   786  
   787  	err = unpinMap.Unpin()
   788  	require.NoError(t, err)
   789  	exist, err = unpinMap.exist()
   790  	require.NoError(t, err)
   791  	require.False(t, exist)
   792  
   793  	err = unpinMap.UnpinIfExists()
   794  	require.NoError(t, err)
   795  	exist, err = unpinMap.exist()
   796  	require.NoError(t, err)
   797  	require.False(t, exist)
   798  
   799  	err = unpinMap.Unpin()
   800  	require.NoError(t, err)
   801  	err = unpinMap.OpenOrCreate()
   802  	require.NoError(t, err)
   803  	err = unpinMap.Unpin()
   804  	require.NoError(t, err)
   805  	exist, err = unpinMap.exist()
   806  	require.NoError(t, err)
   807  	require.False(t, exist)
   808  }
   809  
   810  func TestCreateUnpinned(t *testing.T) {
   811  	setup(t)
   812  
   813  	m := NewMap("cilium_test_create_unpinned",
   814  		ebpf.Hash,
   815  		&TestKey{},
   816  		&TestValue{},
   817  		maxEntries,
   818  		BPF_F_NO_PREALLOC).WithCache()
   819  	err := m.CreateUnpinned()
   820  	require.NoError(t, err)
   821  	exist, err := m.exist()
   822  	require.NoError(t, err)
   823  	require.False(t, exist)
   824  
   825  	k := &TestKey{Key: 105}
   826  	v := &TestValue{Value: 205}
   827  	err = m.Update(k, v)
   828  	require.NoError(t, err)
   829  
   830  	got, err := m.Lookup(k)
   831  	require.NoError(t, err)
   832  	require.EqualValues(t, v, got)
   833  }
   834  
   835  func BenchmarkMapLookup(b *testing.B) {
   836  	b.ReportAllocs()
   837  
   838  	m := NewMap("",
   839  		ebpf.Hash,
   840  		&TestKey{},
   841  		&TestValue{},
   842  		1,
   843  		BPF_F_NO_PREALLOC)
   844  
   845  	if err := m.CreateUnpinned(); err != nil {
   846  		b.Fatal(err)
   847  	}
   848  
   849  	k := TestKey{Key: 0}
   850  	if err := m.Update(&k, &TestValue{Value: 1}); err != nil {
   851  		b.Fatal(err)
   852  	}
   853  
   854  	b.ResetTimer()
   855  
   856  	for n := 0; n < b.N; n++ {
   857  		if _, err := m.Lookup(&k); err != nil {
   858  			b.Fatal(err)
   859  		}
   860  	}
   861  }
   862  
   863  func TestErrorResolver(t *testing.T) {
   864  	testutils.PrivilegedTest(t)
   865  	CheckOrMountFS("")
   866  	require.NoError(t, rlimit.RemoveMemlock())
   867  
   868  	var (
   869  		key1, key2 = TestKey{Key: 10}, TestKey{Key: 20}
   870  		val1, val2 = TestValue{1}, TestValue{2}
   871  	)
   872  
   873  	tests := []struct {
   874  		name        string
   875  		remove      func(t *testing.T, m *Map)
   876  		expectedKey TestKey
   877  		expectedVal TestValue
   878  	}{
   879  		{
   880  			name:        "remove inserted element",
   881  			remove:      func(t *testing.T, m *Map) { require.NoError(t, m.Delete(&key1), "Failed to remove element from map") },
   882  			expectedKey: key2,
   883  			expectedVal: val2,
   884  		},
   885  		{
   886  			name:        "remove failing element",
   887  			remove:      func(t *testing.T, m *Map) { require.Error(t, m.Delete(&key2), "Removal from map should have failed") },
   888  			expectedKey: key1,
   889  			expectedVal: val1,
   890  		},
   891  	}
   892  
   893  	for _, tt := range tests {
   894  		t.Run(tt.name, func(t *testing.T) {
   895  			m := NewMap("cilium_error_resolver_test",
   896  				ebpf.Hash,
   897  				&TestKey{},
   898  				&TestValue{},
   899  				1, // Only one entry, so that the second insertion will fail
   900  				BPF_F_NO_PREALLOC,
   901  			).WithCache()
   902  
   903  			t.Cleanup(func() {
   904  				// Let's make sure that there's no interference between tests
   905  				mapControllers.RemoveControllerAndWait(m.controllerName())
   906  			})
   907  
   908  			require.NoError(t, m.CreateUnpinned(), "Failed to create map")
   909  			require.NoError(t, m.Update(&key1, &val1), "Failed to insert element in map")
   910  
   911  			// Let's attempt to insert a second element in the map, which will fail because the map can only hold one
   912  			require.Error(t, m.Update(&key2, &val2), "Map insertion should have failed")
   913  
   914  			// Let's now remove one of the two elements (the actual assertion depends on which element is to be removed)
   915  			tt.remove(t, m)
   916  
   917  			// Assert that the other element is eventually present and correct
   918  			require.EventuallyWithT(t, func(c *assert.CollectT) {
   919  				value, err := m.Lookup(&tt.expectedKey)
   920  				assert.NoError(c, err)
   921  				if assert.NotNil(c, value) {
   922  					assert.Equal(c, tt.expectedVal.Value, value.(*TestValue).Value)
   923  				}
   924  			}, timeout, tick)
   925  
   926  			// Check that the error resolver controller eventually succeeds
   927  			require.EventuallyWithT(t, func(c *assert.CollectT) {
   928  				models := mapControllers.GetStatusModel()
   929  				for _, model := range models {
   930  					if model.Name == m.controllerName() {
   931  						assert.NotZero(c, model.Status.SuccessCount)
   932  						assert.Greater(c, model.Status.LastSuccessTimestamp, model.Status.LastFailureTimestamp)
   933  						return
   934  					}
   935  				}
   936  
   937  				assert.Fail(c, "Expected controller status not found")
   938  			}, timeout, tick)
   939  		})
   940  	}
   941  }