github.com/lalkh/containerd@v1.4.3/container_checkpoint_test.go (about) 1 // +build linux 2 3 /* 4 Copyright The containerd Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package containerd 20 21 import ( 22 "bytes" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 "strings" 29 "sync" 30 "syscall" 31 "testing" 32 33 "github.com/containerd/containerd/cio" 34 "github.com/containerd/containerd/oci" 35 "github.com/containerd/containerd/plugin" 36 ) 37 38 const ( 39 testCheckpointName = "checkpoint-test:latest" 40 ) 41 42 func TestCheckpointRestorePTY(t *testing.T) { 43 if !supportsCriu { 44 t.Skip("system does not have criu installed") 45 } 46 client, err := newClient(t, address) 47 if err != nil { 48 t.Fatal(err) 49 } 50 defer client.Close() 51 if client.runtime == plugin.RuntimeLinuxV1 { 52 t.Skip() 53 } 54 55 var ( 56 ctx, cancel = testContext(t) 57 id = t.Name() 58 ) 59 defer cancel() 60 61 image, err := client.GetImage(ctx, testImage) 62 if err != nil { 63 t.Fatal(err) 64 } 65 container, err := client.NewContainer(ctx, id, 66 WithNewSnapshot(id, image), 67 WithNewSpec(oci.WithImageConfig(image), 68 oci.WithProcessArgs("sh", "-c", "read A; echo z${A}z"), 69 oci.WithTTY), 70 ) 71 if err != nil { 72 t.Fatal(err) 73 } 74 defer container.Delete(ctx, WithSnapshotCleanup) 75 76 direct, err := newDirectIO(ctx, true) 77 if err != nil { 78 t.Fatal(err) 79 } 80 defer direct.Delete() 81 82 task, err := container.NewTask(ctx, direct.IOCreate) 83 if err != nil { 84 t.Fatal(err) 85 } 86 defer task.Delete(ctx) 87 88 statusC, err := task.Wait(ctx) 89 if err != nil { 90 t.Fatal(err) 91 } 92 93 if err := task.Start(ctx); err != nil { 94 t.Fatal(err) 95 } 96 97 checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"withpty", []CheckpointOpts{ 98 WithCheckpointRuntime, 99 WithCheckpointRW, 100 WithCheckpointTaskExit, 101 WithCheckpointTask, 102 }...) 103 if err != nil { 104 t.Fatal(err) 105 } 106 107 <-statusC 108 109 if _, err := task.Delete(ctx); err != nil { 110 t.Fatal(err) 111 } 112 direct.Delete() 113 if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { 114 t.Fatal(err) 115 } 116 117 direct, err = newDirectIO(ctx, true) 118 if err != nil { 119 t.Fatal(err) 120 } 121 122 var ( 123 wg sync.WaitGroup 124 buf = bytes.NewBuffer(nil) 125 ) 126 wg.Add(1) 127 go func() { 128 defer wg.Done() 129 io.Copy(buf, direct.Stdout) 130 }() 131 132 if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{ 133 WithRestoreImage, 134 WithRestoreSpec, 135 WithRestoreRuntime, 136 WithRestoreRW, 137 }...); err != nil { 138 t.Fatal(err) 139 } 140 if task, err = container.NewTask(ctx, direct.IOCreate, 141 WithTaskCheckpoint(checkpoint)); err != nil { 142 t.Fatal(err) 143 } 144 145 statusC, err = task.Wait(ctx) 146 if err != nil { 147 t.Fatal(err) 148 } 149 150 if err := task.Start(ctx); err != nil { 151 t.Fatal(err) 152 } 153 154 direct.Stdin.Write([]byte("hello\n")) 155 <-statusC 156 wg.Wait() 157 158 if err := direct.Close(); err != nil { 159 t.Error(err) 160 } 161 162 out := buf.String() 163 if !strings.Contains(fmt.Sprintf("%#q", out), `zhelloz`) { 164 t.Fatalf(`expected \x00 in output: %s`, out) 165 } 166 } 167 168 func TestCheckpointRestore(t *testing.T) { 169 if !supportsCriu { 170 t.Skip("system does not have criu installed") 171 } 172 client, err := newClient(t, address) 173 if err != nil { 174 t.Fatal(err) 175 } 176 defer client.Close() 177 if client.runtime == plugin.RuntimeLinuxV1 { 178 t.Skip() 179 } 180 181 var ( 182 ctx, cancel = testContext(t) 183 id = t.Name() 184 ) 185 defer cancel() 186 187 image, err := client.GetImage(ctx, testImage) 188 if err != nil { 189 t.Fatal(err) 190 } 191 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "10"))) 192 if err != nil { 193 t.Fatal(err) 194 } 195 defer container.Delete(ctx, WithSnapshotCleanup) 196 197 task, err := container.NewTask(ctx, empty()) 198 if err != nil { 199 t.Fatal(err) 200 } 201 defer task.Delete(ctx) 202 203 statusC, err := task.Wait(ctx) 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 if err := task.Start(ctx); err != nil { 209 t.Fatal(err) 210 } 211 212 checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"restore", []CheckpointOpts{ 213 WithCheckpointRuntime, 214 WithCheckpointRW, 215 WithCheckpointTask, 216 }...) 217 if err != nil { 218 t.Fatal(err) 219 } 220 221 <-statusC 222 223 if _, err := task.Delete(ctx); err != nil { 224 t.Fatal(err) 225 } 226 if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { 227 t.Fatal(err) 228 } 229 230 if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{ 231 WithRestoreImage, 232 WithRestoreSpec, 233 WithRestoreRuntime, 234 WithRestoreRW, 235 }...); err != nil { 236 t.Fatal(err) 237 } 238 if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil { 239 t.Fatal(err) 240 } 241 defer task.Delete(ctx) 242 243 statusC, err = task.Wait(ctx) 244 if err != nil { 245 t.Fatal(err) 246 } 247 248 if err := task.Start(ctx); err != nil { 249 t.Fatal(err) 250 } 251 252 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 253 t.Fatal(err) 254 } 255 <-statusC 256 } 257 258 func TestCheckpointRestoreNewContainer(t *testing.T) { 259 if !supportsCriu { 260 t.Skip("system does not have criu installed") 261 } 262 client, err := newClient(t, address) 263 if err != nil { 264 t.Fatal(err) 265 } 266 defer client.Close() 267 if client.runtime == plugin.RuntimeLinuxV1 { 268 t.Skip() 269 } 270 271 id := t.Name() 272 ctx, cancel := testContext(t) 273 defer cancel() 274 275 image, err := client.GetImage(ctx, testImage) 276 if err != nil { 277 t.Fatal(err) 278 } 279 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "5"))) 280 if err != nil { 281 t.Fatal(err) 282 } 283 defer container.Delete(ctx, WithSnapshotCleanup) 284 285 task, err := container.NewTask(ctx, empty()) 286 if err != nil { 287 t.Fatal(err) 288 } 289 defer task.Delete(ctx) 290 291 statusC, err := task.Wait(ctx) 292 if err != nil { 293 t.Fatal(err) 294 } 295 296 if err := task.Start(ctx); err != nil { 297 t.Fatal(err) 298 } 299 300 checkpoint, err := container.Checkpoint(ctx, testCheckpointName+"newcontainer", []CheckpointOpts{ 301 WithCheckpointRuntime, 302 WithCheckpointRW, 303 WithCheckpointTask, 304 }...) 305 if err != nil { 306 t.Fatal(err) 307 } 308 309 <-statusC 310 311 if _, err := task.Delete(ctx); err != nil { 312 t.Fatal(err) 313 } 314 if err := container.Delete(ctx, WithSnapshotCleanup); err != nil { 315 t.Fatal(err) 316 } 317 if container, err = client.Restore(ctx, id, checkpoint, []RestoreOpts{ 318 WithRestoreImage, 319 WithRestoreSpec, 320 WithRestoreRuntime, 321 WithRestoreRW, 322 }...); err != nil { 323 t.Fatal(err) 324 } 325 if task, err = container.NewTask(ctx, empty(), WithTaskCheckpoint(checkpoint)); err != nil { 326 t.Fatal(err) 327 } 328 defer task.Delete(ctx) 329 330 statusC, err = task.Wait(ctx) 331 if err != nil { 332 t.Fatal(err) 333 } 334 335 if err := task.Start(ctx); err != nil { 336 t.Fatal(err) 337 } 338 339 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 340 t.Fatal(err) 341 } 342 <-statusC 343 } 344 345 func TestCheckpointLeaveRunning(t *testing.T) { 346 if testing.Short() { 347 t.Skip() 348 } 349 if !supportsCriu { 350 t.Skip("system does not have criu installed") 351 } 352 client, err := newClient(t, address) 353 if err != nil { 354 t.Fatal(err) 355 } 356 defer client.Close() 357 if client.runtime == plugin.RuntimeLinuxV1 { 358 t.Skip() 359 } 360 361 var ( 362 ctx, cancel = testContext(t) 363 id = t.Name() 364 ) 365 defer cancel() 366 367 image, err := client.GetImage(ctx, testImage) 368 if err != nil { 369 t.Fatal(err) 370 } 371 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("sleep", "100"))) 372 if err != nil { 373 t.Fatal(err) 374 } 375 defer container.Delete(ctx, WithSnapshotCleanup) 376 377 task, err := container.NewTask(ctx, empty()) 378 if err != nil { 379 t.Fatal(err) 380 } 381 defer task.Delete(ctx) 382 383 statusC, err := task.Wait(ctx) 384 if err != nil { 385 t.Fatal(err) 386 } 387 388 if err := task.Start(ctx); err != nil { 389 t.Fatal(err) 390 } 391 392 // checkpoint 393 if _, err := container.Checkpoint(ctx, testCheckpointName+"leaverunning", []CheckpointOpts{ 394 WithCheckpointRuntime, 395 WithCheckpointRW, 396 WithCheckpointTask, 397 }...); err != nil { 398 t.Fatal(err) 399 } 400 401 status, err := task.Status(ctx) 402 if err != nil { 403 t.Fatal(err) 404 } 405 if status.Status != Running { 406 t.Fatalf("expected status %q but received %q", Running, status) 407 } 408 409 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 410 t.Fatal(err) 411 } 412 413 <-statusC 414 } 415 416 func TestCRWithImagePath(t *testing.T) { 417 if !supportsCriu { 418 t.Skip("system does not have criu installed") 419 } 420 421 client, err := newClient(t, address) 422 if err != nil { 423 t.Fatal(err) 424 } 425 defer client.Close() 426 427 var ( 428 ctx, cancel = testContext(t) 429 id = t.Name() + "-checkpoint" 430 ) 431 defer cancel() 432 433 image, err := client.GetImage(ctx, testImage) 434 if err != nil { 435 t.Fatal(err) 436 } 437 438 container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), oci.WithProcessArgs("top"))) 439 if err != nil { 440 t.Fatal(err) 441 } 442 defer container.Delete(ctx, WithSnapshotCleanup) 443 444 task, err := container.NewTask(ctx, empty()) 445 if err != nil { 446 t.Fatal(err) 447 } 448 statusC, err := task.Wait(ctx) 449 if err != nil { 450 t.Fatal(err) 451 } 452 if err := task.Start(ctx); err != nil { 453 t.Fatal(err) 454 } 455 456 // create image path store criu image files 457 crDir, err := ioutil.TempDir("", "test-cr") 458 if err != nil { 459 t.Fatal(err) 460 } 461 defer os.RemoveAll(crDir) 462 imagePath := filepath.Join(crDir, "cr") 463 // checkpoint task 464 if _, err := task.Checkpoint(ctx, WithCheckpointImagePath(imagePath)); err != nil { 465 t.Fatal(err) 466 } 467 468 if err := task.Kill(ctx, syscall.SIGKILL); err != nil { 469 t.Fatal(err) 470 } 471 <-statusC 472 task.Delete(ctx) 473 474 // check image files have been dumped into image path 475 if files, err := ioutil.ReadDir(imagePath); err != nil || len(files) == 0 { 476 t.Fatal("failed to checkpoint with image path set") 477 } 478 479 // restore task with same container image and checkpoint directory, 480 // the restore process should finish in millisecond level 481 id = t.Name() + "-restore" 482 ncontainer, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image))) 483 if err != nil { 484 t.Fatal(err) 485 } 486 defer ncontainer.Delete(ctx, WithSnapshotCleanup) 487 488 ntask, err := ncontainer.NewTask(ctx, empty(), WithRestoreImagePath(imagePath)) 489 if err != nil { 490 t.Fatal(err) 491 } 492 statusC, err = ntask.Wait(ctx) 493 if err != nil { 494 t.Fatal(err) 495 } 496 if err := ntask.Start(ctx); err != nil { 497 t.Fatal(err) 498 } 499 500 // check top process is existed in restored container 501 spec, err := container.Spec(ctx) 502 if err != nil { 503 t.Fatal(err) 504 } 505 506 stdout := bytes.NewBuffer(nil) 507 spec.Process.Args = []string{"ps", "-ef"} 508 process, err := ntask.Exec(ctx, t.Name()+"_exec", spec.Process, cio.NewCreator(withByteBuffers(stdout))) 509 if err != nil { 510 t.Fatal(err) 511 } 512 processStatusC, err := process.Wait(ctx) 513 if err != nil { 514 t.Fatal(err) 515 } 516 if err := process.Start(ctx); err != nil { 517 t.Fatal(err) 518 } 519 <-processStatusC 520 if _, err := process.Delete(ctx); err != nil { 521 t.Fatal(err) 522 } 523 524 if !strings.Contains(stdout.String(), "top") { 525 t.Errorf("except top process exists in restored container but not, got output %s", stdout.String()) 526 } 527 528 // we wrote the same thing after attach 529 if err := ntask.Kill(ctx, syscall.SIGKILL); err != nil { 530 t.Fatal(err) 531 } 532 <-statusC 533 ntask.Delete(ctx) 534 }