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 }