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