From bbe1063a5ae681ad04a049518c6087421b919f2e Mon Sep 17 00:00:00 2001 From: Radostin Stoyanov Date: Tue, 12 Apr 2022 18:46:44 +0100 Subject: Add checkpoint image tests The patch introduces the following test cases: 1. An attempt to checkpoint a container that does not exist should fail. 2. Checkpoint of a running container with --create-image should create a checkpoint image. 3. A single checkpoint image can be used to restore multiple containers, each with a different name. 4. Restoring multiple containers from checkpoint images with a single restore command. Signed-off-by: Radostin Stoyanov --- test/e2e/checkpoint_image_test.go | 296 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 test/e2e/checkpoint_image_test.go (limited to 'test/e2e') diff --git a/test/e2e/checkpoint_image_test.go b/test/e2e/checkpoint_image_test.go new file mode 100644 index 000000000..8274dfc80 --- /dev/null +++ b/test/e2e/checkpoint_image_test.go @@ -0,0 +1,296 @@ +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() + podmanTest.SeedImages() + // 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.CheckpointPath).To(Equal("")) + Expect(inspectOut[0].State.CheckpointLog).To(Equal("")) + Expect(inspectOut[0].State.RestoreLog).To(Equal("")) + + 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)) + }) +}) -- cgit v1.2.3-54-g00ecf