whatcanGOwrong

This commit is contained in:
2024-09-19 21:38:24 -04:00
commit d0ae4d841d
17908 changed files with 4096831 additions and 0 deletions
@@ -0,0 +1,21 @@
package testutils
import (
"os"
"testing"
)
// TempBPFFS creates a temporary directory on a BPF FS.
//
// The directory is automatically cleaned up at the end of the test run.
func TempBPFFS(tb testing.TB) string {
tb.Helper()
tmp, err := os.MkdirTemp("/sys/fs/bpf", "ebpf-test")
if err != nil {
tb.Fatal("Create temporary directory on BPFFS:", err)
}
tb.Cleanup(func() { os.RemoveAll(tmp) })
return tmp
}
@@ -0,0 +1,65 @@
package testutils
import (
"errors"
"os"
"strings"
"testing"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/unix"
)
var cgroup2Path = internal.Memoize(func() (string, error) {
mounts, err := os.ReadFile("/proc/mounts")
if err != nil {
return "", err
}
for _, line := range strings.Split(string(mounts), "\n") {
mount := strings.SplitN(line, " ", 3)
if mount[0] == "cgroup2" {
return mount[1], nil
}
continue
}
return "", errors.New("cgroup2 not mounted")
})
func CreateCgroup(tb testing.TB) *os.File {
tb.Helper()
cg2, err := cgroup2Path()
if err != nil {
tb.Fatal("Can't locate cgroup2 mount:", err)
}
cgdir, err := os.MkdirTemp(cg2, "ebpf-link")
if err != nil {
tb.Fatal("Can't create cgroupv2:", err)
}
cgroup, err := os.Open(cgdir)
if err != nil {
os.Remove(cgdir)
tb.Fatal(err)
}
tb.Cleanup(func() {
cgroup.Close()
os.Remove(cgdir)
})
return cgroup
}
func GetCgroupIno(t *testing.T, cgroup *os.File) uint64 {
cgroupStat := unix.Stat_t{}
err := unix.Fstat(int(cgroup.Fd()), &cgroupStat)
if err != nil {
t.Fatal(err)
}
return cgroupStat.Ino
}
@@ -0,0 +1,34 @@
package fdtrace
import (
"fmt"
"os"
"runtime"
"testing"
"github.com/cilium/ebpf/internal/sys"
)
// TestMain runs m with sys.FD leak tracing enabled.
func TestMain(m *testing.M) {
// fn can either be invoked asynchronously by the gc or during disabling of
// the leak tracer below. Don't terminate the program immediately, instead
// capture a boolean that will be used to set the exit code. This avoids races
// and gives all events the chance to be written to stderr.
var leak bool
sys.OnLeakFD(func(fs *runtime.Frames) {
fmt.Fprintln(os.Stderr, "leaked fd created at:")
fmt.Fprintln(os.Stderr, sys.FormatFrames(fs))
leak = true
})
ret := m.Run()
sys.OnLeakFD(nil)
if leak {
ret = 99
}
os.Exit(ret)
}
@@ -0,0 +1,139 @@
package testutils
import (
"errors"
"os"
"strings"
"testing"
"github.com/cilium/ebpf/internal"
)
const (
ignoreKernelVersionEnvVar = "EBPF_TEST_IGNORE_KERNEL_VERSION"
)
func CheckFeatureTest(t *testing.T, fn func() error) {
checkFeatureTestError(t, fn())
}
func checkFeatureTestError(t *testing.T, err error) {
if err == nil {
return
}
var ufe *internal.UnsupportedFeatureError
if errors.As(err, &ufe) {
if ignoreKernelVersionCheck(t.Name()) {
t.Skipf("Ignoring error due to %s: %s", ignoreKernelVersionEnvVar, ufe.Error())
} else {
checkKernelVersion(t, ufe)
}
} else {
t.Error("Feature test failed:", err)
}
}
func CheckFeatureMatrix[K comparable](t *testing.T, fm internal.FeatureMatrix[K]) {
t.Helper()
for key, ft := range fm {
t.Run(ft.Name, func(t *testing.T) {
checkFeatureTestError(t, fm.Result(key))
})
}
}
func SkipIfNotSupported(tb testing.TB, err error) {
tb.Helper()
if err == internal.ErrNotSupported {
tb.Fatal("Unwrapped ErrNotSupported")
}
var ufe *internal.UnsupportedFeatureError
if errors.As(err, &ufe) {
checkKernelVersion(tb, ufe)
tb.Skip(ufe.Error())
}
if errors.Is(err, internal.ErrNotSupported) {
tb.Skip(err.Error())
}
}
func checkKernelVersion(tb testing.TB, ufe *internal.UnsupportedFeatureError) {
if ufe.MinimumVersion.Unspecified() {
return
}
if !isKernelLessThan(tb, ufe.MinimumVersion) {
tb.Helper()
tb.Fatalf("Feature '%s' isn't supported even though kernel is newer than %s",
ufe.Name, ufe.MinimumVersion)
}
}
func SkipOnOldKernel(tb testing.TB, minVersion, feature string) {
tb.Helper()
if IsKernelLessThan(tb, minVersion) {
tb.Skipf("Test requires at least kernel %s (due to missing %s)", minVersion, feature)
}
}
func IsKernelLessThan(tb testing.TB, minVersion string) bool {
tb.Helper()
minv, err := internal.NewVersion(minVersion)
if err != nil {
tb.Fatalf("Invalid version %s: %s", minVersion, err)
}
return isKernelLessThan(tb, minv)
}
func isKernelLessThan(tb testing.TB, minv internal.Version) bool {
tb.Helper()
if max := os.Getenv("CI_MAX_KERNEL_VERSION"); max != "" {
maxv, err := internal.NewVersion(max)
if err != nil {
tb.Fatalf("Invalid version %q in CI_MAX_KERNEL_VERSION: %s", max, err)
}
if maxv.Less(minv) {
tb.Fatalf("Test for %s will never execute on CI since %s is the most recent kernel", minv, maxv)
}
}
return kernelVersion(tb).Less(minv)
}
func kernelVersion(tb testing.TB) internal.Version {
tb.Helper()
v, err := internal.KernelVersion()
if err != nil {
tb.Fatal(err)
}
return v
}
// ignoreKernelVersionCheck checks if test name should be ignored for kernel version check by checking against environment var EBPF_TEST_IGNORE_KERNEL_VERSION.
// EBPF_TEST_IGNORE_KERNEL_VERSION is a comma (,) separated list of test names for which kernel version check should be ignored.
//
// eg: EBPF_TEST_IGNORE_KERNEL_VERSION=TestABC,TestXYZ
func ignoreKernelVersionCheck(tName string) bool {
tNames := os.Getenv(ignoreKernelVersionEnvVar)
if tNames == "" {
return false
}
ignored := strings.Split(tNames, ",")
for _, n := range ignored {
if strings.TrimSpace(n) == tName {
return true
}
}
return false
}
@@ -0,0 +1,54 @@
package testutils
import (
"testing"
)
func TestIgnoreKernelVersionCheckWhenEnvVarIsSet(t *testing.T) {
tests := []struct {
name string
toIgnoreNamesEnvValue string
testName string
ignoreKernelVersionCheck bool
}{
{
name: "should NOT ignore kernel version check if environment var set to empty string",
toIgnoreNamesEnvValue: "",
testName: "TestABC",
ignoreKernelVersionCheck: false,
},
{
name: "should ignore kernel version check if environment var set to skip test name with single value",
toIgnoreNamesEnvValue: "TestABC",
testName: "TestABC",
ignoreKernelVersionCheck: true,
},
{
name: "should match test name when multiple comma separated names list is provided",
toIgnoreNamesEnvValue: "TestABC,TestXYZ",
testName: "TestXYZ",
ignoreKernelVersionCheck: true,
},
{
name: "should NOT match test name when multiple comma separated names list is provided but name is not present in list",
toIgnoreNamesEnvValue: "TestABC,TestXYZ",
testName: "TestPQR",
ignoreKernelVersionCheck: false,
},
{
name: "should match test name if names list has leading/trailing spaces",
toIgnoreNamesEnvValue: "TestABC, TestXYZ , TestPQR",
testName: "TestXYZ",
ignoreKernelVersionCheck: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(ignoreKernelVersionEnvVar, tt.toIgnoreNamesEnvValue)
if got := ignoreKernelVersionCheck(tt.testName); got != tt.ignoreKernelVersionCheck {
t.Errorf("ignoreKernelVersionCheck() = %v, want %v", got, tt.ignoreKernelVersionCheck)
}
})
}
}
@@ -0,0 +1,58 @@
package testutils
import (
"path/filepath"
"testing"
)
// Files calls fn for each given file.
//
// The function errors out if the pattern matches no files.
func Files(t *testing.T, files []string, fn func(*testing.T, string)) {
t.Helper()
if len(files) == 0 {
t.Fatalf("No files given")
}
for _, f := range files {
file := f // force copy
name := filepath.Base(file)
t.Run(name, func(t *testing.T) {
fn(t, file)
})
}
}
// Glob finds files matching a pattern.
//
// The pattern should may include full path. Excludes use the same syntax as
// pattern, but are only applied to the basename instead of the full path.
func Glob(tb testing.TB, pattern string, excludes ...string) []string {
tb.Helper()
files, err := filepath.Glob(pattern)
if err != nil {
tb.Fatal("Can't glob files:", err)
}
if len(excludes) == 0 {
return files
}
var filtered []string
nextFile:
for _, file := range files {
base := filepath.Base(file)
for _, exclude := range excludes {
if matched, err := filepath.Match(exclude, base); err != nil {
tb.Fatal(err)
} else if matched {
continue nextFile
}
}
filtered = append(filtered, file)
}
return filtered
}
@@ -0,0 +1,16 @@
package testutils
import (
"fmt"
"os"
"github.com/cilium/ebpf/rlimit"
)
func init() {
// Increase the memlock for all tests unconditionally. It's a great source of
// weird bugs, since different distros have different default limits.
if err := rlimit.RemoveMemlock(); err != nil {
fmt.Fprintln(os.Stderr, "WARNING: Failed to adjust rlimit, tests may fail")
}
}
@@ -0,0 +1,21 @@
package testutils
import (
"fmt"
"math/rand"
"sync"
"time"
)
var randSeed struct {
value int64
once sync.Once
}
func Rand() *rand.Rand {
randSeed.once.Do(func() {
randSeed.value = time.Now().UnixMicro()
fmt.Printf("Random seed is %d\n", randSeed.value)
})
return rand.New(rand.NewSource(randSeed.value))
}