package integration import ( "os" "os/exec" "strconv" "strings" "github.com/containers/podman/v4/pkg/criu" . "github.com/containers/podman/v4/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman checkpoint", func() { var ( tempdir string err error podmanTest *PodmanTestIntegration ) BeforeEach(func() { SkipIfRootless("checkpoint not supported in rootless mode") tempdir, err = CreateTempDirInTempDir() if err != nil { os.Exit(1) } podmanTest = PodmanTestCreate(tempdir) podmanTest.Setup() // Check if the runtime implements checkpointing. Currently only // runc's checkpoint/restore implementation is supported. cmd := exec.Command(podmanTest.OCIRuntime, "checkpoint", "--help") if err := cmd.Start(); err != nil { Skip("OCI runtime does not support checkpoint/restore") } if err := cmd.Wait(); err != nil { Skip("OCI runtime does not support checkpoint/restore") } if !criu.CheckForCriu(criu.MinCriuVersion) { Skip("CRIU is missing or too old.") } }) AfterEach(func() { podmanTest.Cleanup() f := CurrentGinkgoTestDescription() processTestResult(f) }) It("podman checkpoint --create-image with bogus container", func() { checkpointImage := "foobar-checkpoint" session := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "foobar"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("no container with name or ID \"foobar\" found")) }) It("podman checkpoint --create-image with running container", func() { // Container image must be lowercase checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6)) containerName := "alpine-container-" + RandomString(6) localRunString := []string{ "run", "-it", "-d", "--ip", GetRandomIPAddress(), "--name", containerName, ALPINE, "top", } session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) containerID := session.OutputToString() // Checkpoint image should not exist session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeFalse()) // Check if none of the checkpoint/restore specific information is displayed // for newly started containers. inspect := podmanTest.Podman([]string{"inspect", containerID}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) inspectOut := inspect.InspectContainerToJSON() Expect(inspectOut[0].State.Checkpointed).To(BeFalse(), ".State.Checkpointed") Expect(inspectOut[0].State.Restored).To(BeFalse(), ".State.Restored") Expect(inspectOut[0].State).To(HaveField("CheckpointPath", "")) Expect(inspectOut[0].State).To(HaveField("CheckpointLog", "")) Expect(inspectOut[0].State).To(HaveField("RestoreLog", "")) result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) inspect = podmanTest.Podman([]string{"inspect", containerID}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) inspectOut = inspect.InspectContainerToJSON() Expect(inspectOut[0].State.Checkpointed).To(BeTrue(), ".State.Checkpointed") Expect(inspectOut[0].State.CheckpointPath).To(ContainSubstring("userdata/checkpoint")) Expect(inspectOut[0].State.CheckpointLog).To(ContainSubstring("userdata/dump.log")) // Check if checkpoint image has been created session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeTrue()) // Check if the checkpoint image contains annotations inspect = podmanTest.Podman([]string{"inspect", checkpointImage}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) inspectImageOut := inspect.InspectImageJSON() Expect(inspectImageOut[0].Annotations["io.podman.annotations.checkpoint.name"]).To( BeEquivalentTo(containerName), "io.podman.annotations.checkpoint.name", ) ociRuntimeName := "" if strings.Contains(podmanTest.OCIRuntime, "runc") { ociRuntimeName = "runc" } else if strings.Contains(podmanTest.OCIRuntime, "crun") { ociRuntimeName = "crun" } if ociRuntimeName != "" { Expect(inspectImageOut[0].Annotations["io.podman.annotations.checkpoint.runtime.name"]).To( BeEquivalentTo(ociRuntimeName), "io.podman.annotations.checkpoint.runtime.name", ) } // Remove existing container result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerID}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) // Restore container from checkpoint image result = podmanTest.Podman([]string{"container", "restore", checkpointImage}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) // Clean-up result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) result = podmanTest.Podman([]string{"rmi", checkpointImage}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman restore multiple containers from single checkpint image", func() { // Container image must be lowercase checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6)) containerName := "alpine-container-" + RandomString(6) localRunString := []string{"run", "-d", "--name", containerName, ALPINE, "top"} session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) containerID := session.OutputToString() // Checkpoint image should not exist session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeFalse()) result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) // Check if checkpoint image has been created session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeTrue()) // Remove existing container result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerID}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) for i := 1; i < 5; i++ { // Restore container from checkpoint image name := containerName + strconv.Itoa(i) result = podmanTest.Podman([]string{"container", "restore", "--name", name, checkpointImage}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(i)) // Check that the container is running status := podmanTest.Podman([]string{"inspect", name, "--format={{.State.Status}}"}) status.WaitWithDefaultTimeout() Expect(status).Should(Exit(0)) Expect(status.OutputToString()).To(Equal("running")) } // Clean-up result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) result = podmanTest.Podman([]string{"rmi", checkpointImage}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman restore multiple containers from multiple checkpint images", func() { // Container image must be lowercase checkpointImage1 := "alpine-checkpoint-" + strings.ToLower(RandomString(6)) checkpointImage2 := "alpine-checkpoint-" + strings.ToLower(RandomString(6)) containerName1 := "alpine-container-" + RandomString(6) containerName2 := "alpine-container-" + RandomString(6) // Create first container localRunString := []string{"run", "-d", "--name", containerName1, ALPINE, "top"} session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) containerID1 := session.OutputToString() // Create second container localRunString = []string{"run", "-d", "--name", containerName2, ALPINE, "top"} session = podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) containerID2 := session.OutputToString() // Checkpoint first container result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage1, "--keep", containerID1}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) // Checkpoint second container result = podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage2, "--keep", containerID2}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) // Remove existing containers result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerName1, containerName2}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) // Restore both containers from images result = podmanTest.Podman([]string{"container", "restore", checkpointImage1, checkpointImage2}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // Check if first container is running status := podmanTest.Podman([]string{"inspect", containerName1, "--format={{.State.Status}}"}) status.WaitWithDefaultTimeout() Expect(status).Should(Exit(0)) Expect(status.OutputToString()).To(Equal("running")) // Check if second container is running status = podmanTest.Podman([]string{"inspect", containerName2, "--format={{.State.Status}}"}) status.WaitWithDefaultTimeout() Expect(status).Should(Exit(0)) Expect(status.OutputToString()).To(Equal("running")) // Clean-up result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) result = podmanTest.Podman([]string{"rmi", checkpointImage1, checkpointImage2}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) })