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 })