Skip to content

Commit c5d3309

Browse files
committed
Add a locking mechanism to prevent concurrent subpackage testing
Signed-off-by: apostasie <[email protected]>
1 parent 1a55c72 commit c5d3309

File tree

1 file changed

+53
-2
lines changed

1 file changed

+53
-2
lines changed

pkg/testutil/testutil.go

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ import (
3737
"gotest.tools/v3/icmd"
3838

3939
"github.com/containerd/containerd/v2/defaults"
40+
"github.com/containerd/log"
4041

4142
"github.com/containerd/nerdctl/v2/pkg/buildkitutil"
4243
"github.com/containerd/nerdctl/v2/pkg/imgutil"
4344
"github.com/containerd/nerdctl/v2/pkg/infoutil"
4445
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/dockercompat"
4546
"github.com/containerd/nerdctl/v2/pkg/inspecttypes/native"
47+
"github.com/containerd/nerdctl/v2/pkg/lockutil"
4648
"github.com/containerd/nerdctl/v2/pkg/platformutil"
4749
"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
4850
)
@@ -546,14 +548,63 @@ var (
546548
flagTestKube bool
547549
)
548550

551+
var (
552+
testLockFile = filepath.Join(os.TempDir(), "nerdctl-test-prevent-concurrency", ".lock")
553+
)
554+
549555
func M(m *testing.M) {
550556
flag.StringVar(&flagTestTarget, "test.target", Nerdctl, "target to test")
551557
flag.BoolVar(&flagTestKillDaemon, "test.allow-kill-daemon", false, "enable tests that kill the daemon")
552558
flag.BoolVar(&flagTestIPv6, "test.only-ipv6", false, "enable tests on IPv6")
553559
flag.BoolVar(&flagTestKube, "test.only-kubernetes", false, "enable tests on Kubernetes")
554560
flag.Parse()
555-
fmt.Fprintf(os.Stderr, "test target: %q\n", flagTestTarget)
556-
os.Exit(m.Run())
561+
562+
os.Exit(func() int {
563+
// If there is a lockfile (no err), or if we error-ed stating it (permission), another test run is currently going.
564+
// Note that this could be racy. The .lock file COULD get acquired after this and before we hit the lock section.
565+
// This is not a big deal then: we will just wait for the lock to free.
566+
if _, err := os.Stat(testLockFile); err == nil || !errors.Is(err, os.ErrNotExist) {
567+
log.L.Errorf("Another test binary is already running. If you think this is an error, manually remove %s", testLockFile)
568+
return 1
569+
}
570+
571+
err := os.MkdirAll(filepath.Dir(testLockFile), 0o777)
572+
if err != nil {
573+
log.L.WithError(err).Errorf("failed creating testing lock directory %q", filepath.Dir(testLockFile))
574+
return 1
575+
}
576+
577+
// Ensure that permissions are set to 777 (regardless of umask value), so that we do not lock people out when
578+
// switching between rootful and rootless locking
579+
os.Chmod(filepath.Dir(testLockFile), 0o777)
580+
581+
// Acquire lock
582+
lock, err := lockutil.Lock(filepath.Dir(testLockFile))
583+
if err != nil {
584+
log.L.WithError(err).Errorf("failed acquiring testing lock %q", filepath.Dir(testLockFile))
585+
return 1
586+
}
587+
588+
// Release...
589+
defer lockutil.Unlock(lock)
590+
591+
// Create marker file
592+
err = os.WriteFile(testLockFile, []byte("prevent testing from running in parallel for subpackages integration tests"), 0o666)
593+
if err != nil {
594+
log.L.WithError(err).Errorf("failed writing lock file %q", testLockFile)
595+
return 1
596+
}
597+
598+
// Ensure cleanup
599+
defer func() {
600+
os.Remove(testLockFile)
601+
}()
602+
603+
// Now, run the tests
604+
fmt.Fprintf(os.Stderr, "test target: %q\n", flagTestTarget)
605+
606+
return m.Run()
607+
}())
557608
}
558609

559610
func GetTarget() string {

0 commit comments

Comments
 (0)