diff --git a/.cirrus.yml b/.cirrus.yml index e63ef51cf1a..ccc3d8e6d2c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -79,7 +79,7 @@ task: CIRRUS_WORKING_DIR: /home/runc GO_VERSION: "1.23" BATS_VERSION: "v1.9.0" - RPMS: gcc git iptables jq glibc-static libseccomp-devel make criu fuse-sshfs container-selinux + RPMS: gcc git iptables jq glibc-static libseccomp-devel make fuse-sshfs container-selinux asciidoc gnutls-devel libaio-devel libasan libcap-devel libnet-devel libnl3-devel libbsd-devel libselinux-devel make protobuf-c-devel protobuf-devel python-devel python-PyYAML python-protobuf python-junit_xml python3-importlib-metadata xmlto libdrm-devel # yamllint disable rule:key-duplicates matrix: DISTRO: almalinux-8 @@ -121,13 +121,17 @@ task: # Find out the latest minor release URL. filename=$(curl -fsSL "${PREFIX}?mode=json&include=all" | jq -r --arg Ver "go$GO_VERSION." '. | map(select(.version | contains($Ver))) | first | .files[] | select(.os == "linux" and .arch == "amd64" and .kind == "archive") | .filename') curl -fsSL "$PREFIX$filename" | tar Cxz /usr/local - # install bats + # Install bats cd /tmp git clone https://github.com/bats-core/bats-core cd bats-core git checkout $BATS_VERSION ./install.sh /usr/local cd - + # Testing https://github.com/checkpoint-restore/criu/pull/2545 + git clone https://github.com/kolyshkin/criu.git ~/criu + (cd ~/criu && git checkout freeze-kludges && sudo make install-criu) + rm -rf ~/criu # Add a user for rootless tests useradd -u2000 -m -d/home/rootless -s/bin/bash rootless # Allow root and rootless itself to execute `ssh rootless@localhost` in tests/rootless.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 754fd87f155..bc1dce42977 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,14 +29,6 @@ jobs: race: ["-race", ""] criu: ["", "criu-dev"] exclude: - # Disable most of criu-dev jobs, as they are expensive - # (need to compile criu) and don't add much value/coverage. - - criu: criu-dev - go-version: 1.22.x - - criu: criu-dev - rootless: rootless - - criu: criu-dev - race: -race - go-version: 1.22.x os: actuated-arm64-6cpu-8gb - race: "-race" @@ -124,8 +116,9 @@ jobs: sudo apt -qy install \ libcap-dev libnet1-dev libnl-3-dev \ libprotobuf-c-dev libprotobuf-dev protobuf-c-compiler protobuf-compiler - git clone https://github.com/checkpoint-restore/criu.git ~/criu - (cd ~/criu && git checkout ${{ matrix.criu }} && sudo make install-criu) + # Testing https://github.com/checkpoint-restore/criu/pull/2545 + git clone https://github.com/kolyshkin/criu.git ~/criu + (cd ~/criu && git checkout freeze-kludges && sudo make install-criu) rm -rf ~/criu - name: install go ${{ matrix.go-version }} diff --git a/libcontainer/integration/checkpoint_test.go b/libcontainer/integration/checkpoint_test.go index 8d4d6fe4751..b6428b4f38c 100644 --- a/libcontainer/integration/checkpoint_test.go +++ b/libcontainer/integration/checkpoint_test.go @@ -17,6 +17,8 @@ func criuFeature(feature string) bool { return exec.Command("criu", "check", "--feature", feature).Run() == nil } +const iter = 150 + func TestUsernsCheckpoint(t *testing.T) { testCheckpoint(t, true) } @@ -44,128 +46,132 @@ func testCheckpoint(t *testing.T, userns bool) { t.Skip("Test requires userns") } - config := newTemplateConfig(t, &tParam{userns: userns}) - stateDir := t.TempDir() + for i := range iter { + t.Logf("Iteration %4d of %4d", i, iter) - container, err := libcontainer.Create(stateDir, "test", config) - ok(t, err) - defer destroyContainer(container) + config := newTemplateConfig(t, &tParam{userns: userns}) + stateDir := t.TempDir() - stdinR, stdinW, err := os.Pipe() - ok(t, err) + container, err := libcontainer.Create(stateDir, "test", config) + ok(t, err) + defer destroyContainer(container) - var stdout bytes.Buffer + stdinR, stdinW, err := os.Pipe() + ok(t, err) - pconfig := libcontainer.Process{ - Cwd: "/", - Args: []string{"cat"}, - Env: standardEnvironment, - Stdin: stdinR, - Stdout: &stdout, - Init: true, - } + var stdout bytes.Buffer - err = container.Run(&pconfig) - _ = stdinR.Close() - defer stdinW.Close() //nolint: errcheck - ok(t, err) + pconfig := libcontainer.Process{ + Cwd: "/", + Args: []string{"cat"}, + Env: standardEnvironment, + Stdin: stdinR, + Stdout: &stdout, + Init: true, + } - pid, err := pconfig.Pid() - ok(t, err) + err = container.Run(&pconfig) + _ = stdinR.Close() + defer stdinW.Close() //nolint: errcheck + ok(t, err) - process, err := os.FindProcess(pid) - ok(t, err) + pid, err := pconfig.Pid() + ok(t, err) - tmp := t.TempDir() - var parentImage string + process, err := os.FindProcess(pid) + ok(t, err) - // 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, + 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, + } + + if err := container.Checkpoint(preDumpOpts); err != nil { + t.Fatal(err) + } + + state, err := container.Status() + ok(t, err) + + if state != libcontainer.Running { + t.Fatal("Unexpected preDump state: ", state) + } } - if err := container.Checkpoint(preDumpOpts); err != nil { + imagesDir := filepath.Join(tmp, "criu") + + checkpointOpts := &libcontainer.CriuOpts{ + ImagesDirectory: imagesDir, + WorkDirectory: imagesDir, + ParentImage: parentImage, + } + + if err := container.Checkpoint(checkpointOpts); err != nil { t.Fatal(err) } state, err := container.Status() ok(t, err) - if state != libcontainer.Running { - t.Fatal("Unexpected preDump state: ", state) + if state != libcontainer.Stopped { + t.Fatal("Unexpected state checkpoint: ", state) } - } - - imagesDir := filepath.Join(tmp, "criu") - - checkpointOpts := &libcontainer.CriuOpts{ - ImagesDirectory: imagesDir, - WorkDirectory: imagesDir, - ParentImage: parentImage, - } - - if err := container.Checkpoint(checkpointOpts); err != nil { - t.Fatal(err) - } - - state, err := container.Status() - ok(t, err) - if state != libcontainer.Stopped { - t.Fatal("Unexpected state checkpoint: ", state) - } - - _ = stdinW.Close() - _, err = process.Wait() - ok(t, err) + _ = stdinW.Close() + _, err = process.Wait() + ok(t, err) - // reload the container - container, err = libcontainer.Load(stateDir, "test") - ok(t, err) + // reload the container + container, err = libcontainer.Load(stateDir, "test") + ok(t, err) - restoreStdinR, restoreStdinW, err := os.Pipe() - ok(t, err) + restoreStdinR, restoreStdinW, err := os.Pipe() + ok(t, err) - var restoreStdout bytes.Buffer - restoreProcessConfig := &libcontainer.Process{ - Cwd: "/", - Stdin: restoreStdinR, - Stdout: &restoreStdout, - Init: true, - } + var restoreStdout bytes.Buffer + restoreProcessConfig := &libcontainer.Process{ + Cwd: "/", + Stdin: restoreStdinR, + Stdout: &restoreStdout, + Init: true, + } - err = container.Restore(restoreProcessConfig, checkpointOpts) - _ = restoreStdinR.Close() - defer restoreStdinW.Close() //nolint: errcheck - if err != nil { - t.Fatal(err) - } + err = container.Restore(restoreProcessConfig, checkpointOpts) + _ = restoreStdinR.Close() + defer restoreStdinW.Close() //nolint: errcheck + if err != nil { + t.Fatal(err) + } - state, err = container.Status() - ok(t, err) - if state != libcontainer.Running { - t.Fatal("Unexpected restore state: ", state) - } + state, err = container.Status() + ok(t, err) + if state != libcontainer.Running { + t.Fatal("Unexpected restore state: ", state) + } - pid, err = restoreProcessConfig.Pid() - ok(t, err) + pid, err = restoreProcessConfig.Pid() + ok(t, err) - err = unix.Kill(pid, 0) - ok(t, err) + err = unix.Kill(pid, 0) + ok(t, err) - _, err = restoreStdinW.WriteString("Hello!") - ok(t, err) + _, err = restoreStdinW.WriteString("Hello!") + ok(t, err) - _ = restoreStdinW.Close() - waitProcess(restoreProcessConfig, t) + _ = restoreStdinW.Close() + waitProcess(restoreProcessConfig, t) - output := restoreStdout.String() - if !strings.Contains(output, "Hello!") { - t.Fatal("Did not restore the pipe correctly:", output) + output := restoreStdout.String() + if !strings.Contains(output, "Hello!") { + t.Fatal("Did not restore the pipe correctly:", output) + } } }