github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/checkpoint_image_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"os/exec"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/containers/podman/v5/pkg/criu"
    10  	. "github.com/containers/podman/v5/test/utils"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  )
    14  
    15  var _ = Describe("Podman checkpoint", func() {
    16  
    17  	BeforeEach(func() {
    18  		SkipIfRootless("checkpoint not supported in rootless mode")
    19  		// Check if the runtime implements checkpointing. Currently only
    20  		// runc's checkpoint/restore implementation is supported.
    21  		cmd := exec.Command(podmanTest.OCIRuntime, "checkpoint", "--help")
    22  		if err := cmd.Start(); err != nil {
    23  			Skip("OCI runtime does not support checkpoint/restore")
    24  		}
    25  		if err := cmd.Wait(); err != nil {
    26  			Skip("OCI runtime does not support checkpoint/restore")
    27  		}
    28  
    29  		if err := criu.CheckForCriu(criu.MinCriuVersion); err != nil {
    30  			Skip(fmt.Sprintf("check CRIU version error: %v", err))
    31  		}
    32  	})
    33  
    34  	It("podman checkpoint --create-image with bogus container", func() {
    35  		checkpointImage := "foobar-checkpoint"
    36  		session := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "foobar"})
    37  		session.WaitWithDefaultTimeout()
    38  		Expect(session).To(ExitWithError(125, `no container with name or ID "foobar" found: no such container`))
    39  	})
    40  
    41  	It("podman checkpoint --create-image with running container", func() {
    42  		// Container image must be lowercase
    43  		checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
    44  		containerName := "alpine-container-" + RandomString(6)
    45  
    46  		localRunString := []string{
    47  			"run",
    48  			"-d",
    49  			"--ip", GetSafeIPAddress(),
    50  			"--name", containerName,
    51  			ALPINE,
    52  			"top",
    53  		}
    54  		session := podmanTest.Podman(localRunString)
    55  		session.WaitWithDefaultTimeout()
    56  		Expect(session).Should(ExitCleanly())
    57  		containerID := session.OutputToString()
    58  
    59  		// Checkpoint image should not exist
    60  		session = podmanTest.Podman([]string{"images"})
    61  		session.WaitWithDefaultTimeout()
    62  		Expect(session).Should(ExitCleanly())
    63  		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeFalse())
    64  
    65  		// Check if none of the checkpoint/restore specific information is displayed
    66  		// for newly started containers.
    67  		inspect := podmanTest.Podman([]string{"inspect", containerID})
    68  		inspect.WaitWithDefaultTimeout()
    69  		Expect(inspect).Should(ExitCleanly())
    70  		inspectOut := inspect.InspectContainerToJSON()
    71  		Expect(inspectOut[0].State.Checkpointed).To(BeFalse(), ".State.Checkpointed")
    72  		Expect(inspectOut[0].State.Restored).To(BeFalse(), ".State.Restored")
    73  		Expect(inspectOut[0].State).To(HaveField("CheckpointPath", ""))
    74  		Expect(inspectOut[0].State).To(HaveField("CheckpointLog", ""))
    75  		Expect(inspectOut[0].State).To(HaveField("RestoreLog", ""))
    76  
    77  		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID})
    78  		result.WaitWithDefaultTimeout()
    79  
    80  		Expect(result).Should(ExitCleanly())
    81  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
    82  		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
    83  
    84  		inspect = podmanTest.Podman([]string{"inspect", containerID})
    85  		inspect.WaitWithDefaultTimeout()
    86  		Expect(inspect).Should(ExitCleanly())
    87  		inspectOut = inspect.InspectContainerToJSON()
    88  		Expect(inspectOut[0].State.Checkpointed).To(BeTrue(), ".State.Checkpointed")
    89  		Expect(inspectOut[0].State.CheckpointPath).To(ContainSubstring("userdata/checkpoint"))
    90  		Expect(inspectOut[0].State.CheckpointLog).To(ContainSubstring("userdata/dump.log"))
    91  
    92  		// Check if checkpoint image has been created
    93  		session = podmanTest.Podman([]string{"images"})
    94  		session.WaitWithDefaultTimeout()
    95  		Expect(session).Should(ExitCleanly())
    96  		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeTrue())
    97  
    98  		// Check if the checkpoint image contains annotations
    99  		inspect = podmanTest.Podman([]string{"inspect", checkpointImage})
   100  		inspect.WaitWithDefaultTimeout()
   101  		Expect(inspect).Should(ExitCleanly())
   102  		inspectImageOut := inspect.InspectImageJSON()
   103  		Expect(inspectImageOut[0].Annotations["io.podman.annotations.checkpoint.name"]).To(
   104  			BeEquivalentTo(containerName),
   105  			"io.podman.annotations.checkpoint.name",
   106  		)
   107  
   108  		ociRuntimeName := ""
   109  		if strings.Contains(podmanTest.OCIRuntime, "runc") {
   110  			ociRuntimeName = "runc"
   111  		} else if strings.Contains(podmanTest.OCIRuntime, "crun") {
   112  			ociRuntimeName = "crun"
   113  		}
   114  		if ociRuntimeName != "" {
   115  			Expect(inspectImageOut[0].Annotations["io.podman.annotations.checkpoint.runtime.name"]).To(
   116  				BeEquivalentTo(ociRuntimeName),
   117  				"io.podman.annotations.checkpoint.runtime.name",
   118  			)
   119  		}
   120  
   121  		// Remove existing container
   122  		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerID})
   123  		result.WaitWithDefaultTimeout()
   124  		Expect(result).Should(ExitCleanly())
   125  
   126  		// Restore container from checkpoint image
   127  		result = podmanTest.Podman([]string{"container", "restore", checkpointImage})
   128  		result.WaitWithDefaultTimeout()
   129  
   130  		Expect(result).Should(ExitCleanly())
   131  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
   132  		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
   133  
   134  		// Clean-up
   135  		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
   136  		result.WaitWithDefaultTimeout()
   137  		Expect(result).Should(ExitCleanly())
   138  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   139  
   140  		result = podmanTest.Podman([]string{"rmi", checkpointImage})
   141  		result.WaitWithDefaultTimeout()
   142  		Expect(result).Should(ExitCleanly())
   143  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   144  	})
   145  
   146  	It("podman restore multiple containers from single checkpoint image", func() {
   147  		// Container image must be lowercase
   148  		checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
   149  		containerName := "alpine-container-" + RandomString(6)
   150  
   151  		localRunString := []string{"run", "-d", "--name", containerName, ALPINE, "top"}
   152  		session := podmanTest.Podman(localRunString)
   153  		session.WaitWithDefaultTimeout()
   154  		Expect(session).Should(ExitCleanly())
   155  		containerID := session.OutputToString()
   156  
   157  		// Checkpoint image should not exist
   158  		session = podmanTest.Podman([]string{"images"})
   159  		session.WaitWithDefaultTimeout()
   160  		Expect(session).Should(ExitCleanly())
   161  		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeFalse())
   162  
   163  		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID})
   164  		result.WaitWithDefaultTimeout()
   165  
   166  		Expect(result).Should(ExitCleanly())
   167  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   168  		Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited"))
   169  
   170  		// Check if checkpoint image has been created
   171  		session = podmanTest.Podman([]string{"images"})
   172  		session.WaitWithDefaultTimeout()
   173  		Expect(session).Should(ExitCleanly())
   174  		Expect(session.LineInOutputContainsTag("localhost/"+checkpointImage, "latest")).To(BeTrue())
   175  
   176  		// Remove existing container
   177  		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerID})
   178  		result.WaitWithDefaultTimeout()
   179  		Expect(result).Should(ExitCleanly())
   180  
   181  		for i := 1; i < 5; i++ {
   182  			// Restore container from checkpoint image
   183  			name := containerName + strconv.Itoa(i)
   184  			result = podmanTest.Podman([]string{"container", "restore", "--name", name, checkpointImage})
   185  			result.WaitWithDefaultTimeout()
   186  			Expect(result).Should(ExitCleanly())
   187  			Expect(podmanTest.NumberOfContainersRunning()).To(Equal(i))
   188  
   189  			// Check that the container is running
   190  			status := podmanTest.Podman([]string{"inspect", name, "--format={{.State.Status}}"})
   191  			status.WaitWithDefaultTimeout()
   192  			Expect(status).Should(ExitCleanly())
   193  			Expect(status.OutputToString()).To(Equal("running"))
   194  		}
   195  
   196  		// Clean-up
   197  		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
   198  		result.WaitWithDefaultTimeout()
   199  		Expect(result).Should(ExitCleanly())
   200  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   201  
   202  		result = podmanTest.Podman([]string{"rmi", checkpointImage})
   203  		result.WaitWithDefaultTimeout()
   204  		Expect(result).Should(ExitCleanly())
   205  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   206  	})
   207  
   208  	It("podman restore multiple containers from multiple checkpoint images", func() {
   209  		// Container image must be lowercase
   210  		checkpointImage1 := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
   211  		checkpointImage2 := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
   212  		containerName1 := "alpine-container-" + RandomString(6)
   213  		containerName2 := "alpine-container-" + RandomString(6)
   214  
   215  		// Create first container
   216  		localRunString := []string{"run", "-d", "--name", containerName1, ALPINE, "top"}
   217  		session := podmanTest.Podman(localRunString)
   218  		session.WaitWithDefaultTimeout()
   219  		Expect(session).Should(ExitCleanly())
   220  		containerID1 := session.OutputToString()
   221  
   222  		// Create second container
   223  		localRunString = []string{"run", "-d", "--name", containerName2, ALPINE, "top"}
   224  		session = podmanTest.Podman(localRunString)
   225  		session.WaitWithDefaultTimeout()
   226  		Expect(session).Should(ExitCleanly())
   227  		containerID2 := session.OutputToString()
   228  
   229  		// Checkpoint first container
   230  		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage1, "--keep", containerID1})
   231  		result.WaitWithDefaultTimeout()
   232  		Expect(result).Should(ExitCleanly())
   233  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
   234  
   235  		// Checkpoint second container
   236  		result = podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage2, "--keep", containerID2})
   237  		result.WaitWithDefaultTimeout()
   238  		Expect(result).Should(ExitCleanly())
   239  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   240  
   241  		// Remove existing containers
   242  		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerName1, containerName2})
   243  		result.WaitWithDefaultTimeout()
   244  		Expect(result).Should(ExitCleanly())
   245  
   246  		// Restore both containers from images
   247  		result = podmanTest.Podman([]string{"container", "restore", checkpointImage1, checkpointImage2})
   248  		result.WaitWithDefaultTimeout()
   249  		Expect(result).Should(ExitCleanly())
   250  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
   251  
   252  		// Check if first container is running
   253  		status := podmanTest.Podman([]string{"inspect", containerName1, "--format={{.State.Status}}"})
   254  		status.WaitWithDefaultTimeout()
   255  		Expect(status).Should(ExitCleanly())
   256  		Expect(status.OutputToString()).To(Equal("running"))
   257  
   258  		// Check if second container is running
   259  		status = podmanTest.Podman([]string{"inspect", containerName2, "--format={{.State.Status}}"})
   260  		status.WaitWithDefaultTimeout()
   261  		Expect(status).Should(ExitCleanly())
   262  		Expect(status.OutputToString()).To(Equal("running"))
   263  
   264  		// Clean-up
   265  		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
   266  		result.WaitWithDefaultTimeout()
   267  		Expect(result).Should(ExitCleanly())
   268  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   269  
   270  		result = podmanTest.Podman([]string{"rmi", checkpointImage1, checkpointImage2})
   271  		result.WaitWithDefaultTimeout()
   272  		Expect(result).Should(ExitCleanly())
   273  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   274  	})
   275  
   276  	It("podman run with checkpoint image", func() {
   277  		// Container image must be lowercase
   278  		checkpointImage := "alpine-checkpoint-" + strings.ToLower(RandomString(6))
   279  		containerName := "alpine-container-" + RandomString(6)
   280  
   281  		// Create container
   282  		localRunString := []string{"run", "-d", "--name", containerName, ALPINE, "top"}
   283  		session := podmanTest.Podman(localRunString)
   284  		session.WaitWithDefaultTimeout()
   285  		Expect(session).Should(ExitCleanly())
   286  		containerID1 := session.OutputToString()
   287  
   288  		// Checkpoint container, create checkpoint image
   289  		result := podmanTest.Podman([]string{"container", "checkpoint", "--create-image", checkpointImage, "--keep", containerID1})
   290  		result.WaitWithDefaultTimeout()
   291  		Expect(result).Should(ExitCleanly())
   292  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   293  
   294  		// Remove existing container
   295  		result = podmanTest.Podman([]string{"rm", "-t", "1", "-f", containerName})
   296  		result.WaitWithDefaultTimeout()
   297  		Expect(result).Should(ExitCleanly())
   298  
   299  		// Restore containers from image using `podman run`
   300  		result = podmanTest.Podman([]string{"run", checkpointImage})
   301  		result.WaitWithDefaultTimeout()
   302  		Expect(result).Should(ExitCleanly())
   303  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
   304  
   305  		// Check if the container is running
   306  		status := podmanTest.Podman([]string{"inspect", containerName, "--format={{.State.Status}}"})
   307  		status.WaitWithDefaultTimeout()
   308  		Expect(status).Should(ExitCleanly())
   309  		Expect(status.OutputToString()).To(Equal("running"))
   310  
   311  		// Clean-up
   312  		result = podmanTest.Podman([]string{"rm", "-t", "0", "-fa"})
   313  		result.WaitWithDefaultTimeout()
   314  		Expect(result).Should(ExitCleanly())
   315  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   316  
   317  		result = podmanTest.Podman([]string{"rmi", checkpointImage})
   318  		result.WaitWithDefaultTimeout()
   319  		Expect(result).Should(ExitCleanly())
   320  		Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
   321  	})
   322  })