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