diff --git a/libcontainer/criu_linux.go b/libcontainer/criu_linux.go index 66178fc16f6..fda16bead4c 100644 --- a/libcontainer/criu_linux.go +++ b/libcontainer/criu_linux.go @@ -30,7 +30,7 @@ var criuFeatures *criurpc.CriuFeatures var ErrCriuMissingFeatures = errors.New("criu is missing features") -func (c *Container) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.CriuOpts, criuFeat *criurpc.CriuFeatures) error { +func (c *Container) checkCriuFeatures(criuOpts *CriuOpts, criuFeat *criurpc.CriuFeatures) error { t := criurpc.CriuReqType_FEATURE_CHECK // make sure the features we are looking for are really not from @@ -38,18 +38,13 @@ func (c *Container) checkCriuFeatures(criuOpts *CriuOpts, rpcOpts *criurpc.CriuO criuFeatures = nil req := &criurpc.CriuReq{ - Type: &t, - // Theoretically this should not be necessary but CRIU - // segfaults if Opts is empty. - // Fixed in CRIU 2.12 - Opts: rpcOpts, + Type: &t, Features: criuFeat, } err := c.criuSwrk(nil, req, criuOpts, nil) if err != nil { - logrus.Debugf("%s", err) - return errors.New("CRIU feature check failed") + return fmt.Errorf("CRIU feature check failed: %w", err) } var missingFeatures []string @@ -398,7 +393,7 @@ func (c *Container) Checkpoint(criuOpts *CriuOpts) error { MemTrack: proto.Bool(true), } - if err := c.checkCriuFeatures(criuOpts, &rpcOpts, &feat); err != nil { + if err := c.checkCriuFeatures(criuOpts, &feat); err != nil { return err } @@ -412,7 +407,7 @@ func (c *Container) Checkpoint(criuOpts *CriuOpts) error { feat := criurpc.CriuFeatures{ LazyPages: proto.Bool(true), } - if err := c.checkCriuFeatures(criuOpts, &rpcOpts, &feat); err != nil { + if err := c.checkCriuFeatures(criuOpts, &feat); err != nil { return err } diff --git a/libcontainer/integration/checkpoint_test.go b/libcontainer/integration/checkpoint_test.go index c5426af1983..8d4d6fe4751 100644 --- a/libcontainer/integration/checkpoint_test.go +++ b/libcontainer/integration/checkpoint_test.go @@ -1,9 +1,7 @@ package integration import ( - "bufio" "bytes" - "errors" "os" "os/exec" "path/filepath" @@ -15,38 +13,11 @@ import ( "golang.org/x/sys/unix" ) -func showFile(t *testing.T, fname string) { - t.Helper() - t.Logf("=== %s ===\n", fname) - - f, err := os.Open(fname) - if err != nil { - t.Log(err) - return - } - defer f.Close() //nolint: errcheck - - scanner := bufio.NewScanner(f) - for scanner.Scan() { - t.Log(scanner.Text()) - } - - if err := scanner.Err(); err != nil { - t.Log(err) - return - } - - t.Logf("=== END ===\n") +func criuFeature(feature string) bool { + return exec.Command("criu", "check", "--feature", feature).Run() == nil } func TestUsernsCheckpoint(t *testing.T) { - if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) { - t.Skip("Test requires userns.") - } - cmd := exec.Command("criu", "check", "--feature", "userns") - if err := cmd.Run(); err != nil { - t.Skip("Unable to c/r a container with userns") - } testCheckpoint(t, true) } @@ -69,6 +40,10 @@ func testCheckpoint(t *testing.T, userns bool) { t.Skip("Test requires criu >= 3.17-4 on CentOS Stream 9.") } + if userns && !criuFeature("userns") { + t.Skip("Test requires userns") + } + config := newTemplateConfig(t, &tParam{userns: userns}) stateDir := t.TempDir() @@ -102,28 +77,28 @@ func testCheckpoint(t *testing.T, userns bool) { ok(t, err) tmp := t.TempDir() + var parentImage string + + // Test pre-dump if mem_dirty_track is available. + if criuFeature("mem_dirty_track") { + parentImage = "../criu-parent" + parentDir := filepath.Join(tmp, "criu-parent") + preDumpOpts := &libcontainer.CriuOpts{ + ImagesDirectory: parentDir, + WorkDirectory: parentDir, + PreDump: true, + } - parentDir := filepath.Join(tmp, "criu-parent") - preDumpOpts := &libcontainer.CriuOpts{ - ImagesDirectory: parentDir, - WorkDirectory: parentDir, - PreDump: true, - } - preDumpLog := filepath.Join(preDumpOpts.WorkDirectory, "dump.log") - - if err := container.Checkpoint(preDumpOpts); err != nil { - showFile(t, preDumpLog) - if errors.Is(err, libcontainer.ErrCriuMissingFeatures) { - t.Skip(err) + if err := container.Checkpoint(preDumpOpts); err != nil { + t.Fatal(err) } - t.Fatal(err) - } - state, err := container.Status() - ok(t, err) + state, err := container.Status() + ok(t, err) - if state != libcontainer.Running { - t.Fatal("Unexpected preDump state: ", state) + if state != libcontainer.Running { + t.Fatal("Unexpected preDump state: ", state) + } } imagesDir := filepath.Join(tmp, "criu") @@ -131,17 +106,14 @@ func testCheckpoint(t *testing.T, userns bool) { checkpointOpts := &libcontainer.CriuOpts{ ImagesDirectory: imagesDir, WorkDirectory: imagesDir, - ParentImage: "../criu-parent", + ParentImage: parentImage, } - dumpLog := filepath.Join(checkpointOpts.WorkDirectory, "dump.log") - restoreLog := filepath.Join(checkpointOpts.WorkDirectory, "restore.log") if err := container.Checkpoint(checkpointOpts); err != nil { - showFile(t, dumpLog) t.Fatal(err) } - state, err = container.Status() + state, err := container.Status() ok(t, err) if state != libcontainer.Stopped { @@ -171,7 +143,6 @@ func testCheckpoint(t *testing.T, userns bool) { _ = restoreStdinR.Close() defer restoreStdinW.Close() //nolint: errcheck if err != nil { - showFile(t, restoreLog) t.Fatal(err) }