github.com/lalkh/containerd@v1.4.3/services/tasks/local.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tasks 18 19 import ( 20 "bytes" 21 "context" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "strings" 28 "time" 29 30 api "github.com/containerd/containerd/api/services/tasks/v1" 31 "github.com/containerd/containerd/api/types" 32 "github.com/containerd/containerd/api/types/task" 33 "github.com/containerd/containerd/archive" 34 "github.com/containerd/containerd/containers" 35 "github.com/containerd/containerd/content" 36 "github.com/containerd/containerd/errdefs" 37 "github.com/containerd/containerd/events" 38 "github.com/containerd/containerd/filters" 39 "github.com/containerd/containerd/images" 40 "github.com/containerd/containerd/log" 41 "github.com/containerd/containerd/metadata" 42 "github.com/containerd/containerd/mount" 43 "github.com/containerd/containerd/pkg/timeout" 44 "github.com/containerd/containerd/plugin" 45 "github.com/containerd/containerd/runtime" 46 "github.com/containerd/containerd/runtime/linux/runctypes" 47 v2 "github.com/containerd/containerd/runtime/v2" 48 "github.com/containerd/containerd/runtime/v2/runc/options" 49 "github.com/containerd/containerd/services" 50 "github.com/containerd/typeurl" 51 ptypes "github.com/gogo/protobuf/types" 52 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 53 "github.com/pkg/errors" 54 "google.golang.org/grpc" 55 "google.golang.org/grpc/codes" 56 "google.golang.org/grpc/status" 57 ) 58 59 var ( 60 _ = (api.TasksClient)(&local{}) 61 empty = &ptypes.Empty{} 62 ) 63 64 const ( 65 stateTimeout = "io.containerd.timeout.task.state" 66 ) 67 68 func init() { 69 plugin.Register(&plugin.Registration{ 70 Type: plugin.ServicePlugin, 71 ID: services.TasksService, 72 Requires: tasksServiceRequires, 73 InitFn: initFunc, 74 }) 75 76 timeout.Set(stateTimeout, 2*time.Second) 77 } 78 79 func initFunc(ic *plugin.InitContext) (interface{}, error) { 80 runtimes, err := loadV1Runtimes(ic) 81 if err != nil { 82 return nil, err 83 } 84 85 v2r, err := ic.Get(plugin.RuntimePluginV2) 86 if err != nil { 87 return nil, err 88 } 89 90 m, err := ic.Get(plugin.MetadataPlugin) 91 if err != nil { 92 return nil, err 93 } 94 95 monitor, err := ic.Get(plugin.TaskMonitorPlugin) 96 if err != nil { 97 if !errdefs.IsNotFound(err) { 98 return nil, err 99 } 100 monitor = runtime.NewNoopMonitor() 101 } 102 103 db := m.(*metadata.DB) 104 l := &local{ 105 runtimes: runtimes, 106 containers: metadata.NewContainerStore(db), 107 store: db.ContentStore(), 108 publisher: ic.Events, 109 monitor: monitor.(runtime.TaskMonitor), 110 v2Runtime: v2r.(*v2.TaskManager), 111 } 112 for _, r := range runtimes { 113 tasks, err := r.Tasks(ic.Context, true) 114 if err != nil { 115 return nil, err 116 } 117 for _, t := range tasks { 118 l.monitor.Monitor(t) 119 } 120 } 121 v2Tasks, err := l.v2Runtime.Tasks(ic.Context, true) 122 if err != nil { 123 return nil, err 124 } 125 for _, t := range v2Tasks { 126 l.monitor.Monitor(t) 127 } 128 return l, nil 129 } 130 131 type local struct { 132 runtimes map[string]runtime.PlatformRuntime 133 containers containers.Store 134 store content.Store 135 publisher events.Publisher 136 137 monitor runtime.TaskMonitor 138 v2Runtime *v2.TaskManager 139 } 140 141 func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.CallOption) (*api.CreateTaskResponse, error) { 142 container, err := l.getContainer(ctx, r.ContainerID) 143 if err != nil { 144 return nil, errdefs.ToGRPC(err) 145 } 146 checkpointPath, err := getRestorePath(container.Runtime.Name, r.Options) 147 if err != nil { 148 return nil, err 149 } 150 // jump get checkpointPath from checkpoint image 151 if checkpointPath != "" && r.Checkpoint != nil { 152 checkpointPath, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "ctrd-checkpoint") 153 if err != nil { 154 return nil, err 155 } 156 if r.Checkpoint.MediaType != images.MediaTypeContainerd1Checkpoint { 157 return nil, fmt.Errorf("unsupported checkpoint type %q", r.Checkpoint.MediaType) 158 } 159 reader, err := l.store.ReaderAt(ctx, ocispec.Descriptor{ 160 MediaType: r.Checkpoint.MediaType, 161 Digest: r.Checkpoint.Digest, 162 Size: r.Checkpoint.Size_, 163 Annotations: r.Checkpoint.Annotations, 164 }) 165 if err != nil { 166 return nil, err 167 } 168 _, err = archive.Apply(ctx, checkpointPath, content.NewReader(reader)) 169 reader.Close() 170 if err != nil { 171 return nil, err 172 } 173 } 174 opts := runtime.CreateOpts{ 175 Spec: container.Spec, 176 IO: runtime.IO{ 177 Stdin: r.Stdin, 178 Stdout: r.Stdout, 179 Stderr: r.Stderr, 180 Terminal: r.Terminal, 181 }, 182 Checkpoint: checkpointPath, 183 Runtime: container.Runtime.Name, 184 RuntimeOptions: container.Runtime.Options, 185 TaskOptions: r.Options, 186 } 187 for _, m := range r.Rootfs { 188 opts.Rootfs = append(opts.Rootfs, mount.Mount{ 189 Type: m.Type, 190 Source: m.Source, 191 Options: m.Options, 192 }) 193 } 194 if strings.HasPrefix(container.Runtime.Name, "io.containerd.runtime.v1.") { 195 log.G(ctx).Warn("runtime v1 is deprecated since containerd v1.4, consider using runtime v2") 196 } else if container.Runtime.Name == plugin.RuntimeRuncV1 { 197 log.G(ctx).Warnf("%q is deprecated since containerd v1.4, consider using %q", plugin.RuntimeRuncV1, plugin.RuntimeRuncV2) 198 } 199 rtime, err := l.getRuntime(container.Runtime.Name) 200 if err != nil { 201 return nil, err 202 } 203 _, err = rtime.Get(ctx, r.ContainerID) 204 if err != nil && err != runtime.ErrTaskNotExists { 205 return nil, errdefs.ToGRPC(err) 206 } 207 if err == nil { 208 return nil, errdefs.ToGRPC(fmt.Errorf("task %s already exists", r.ContainerID)) 209 } 210 c, err := rtime.Create(ctx, r.ContainerID, opts) 211 if err != nil { 212 return nil, errdefs.ToGRPC(err) 213 } 214 if err := l.monitor.Monitor(c); err != nil { 215 return nil, errors.Wrap(err, "monitor task") 216 } 217 return &api.CreateTaskResponse{ 218 ContainerID: r.ContainerID, 219 Pid: c.PID(), 220 }, nil 221 } 222 223 func (l *local) Start(ctx context.Context, r *api.StartRequest, _ ...grpc.CallOption) (*api.StartResponse, error) { 224 t, err := l.getTask(ctx, r.ContainerID) 225 if err != nil { 226 return nil, err 227 } 228 p := runtime.Process(t) 229 if r.ExecID != "" { 230 if p, err = t.Process(ctx, r.ExecID); err != nil { 231 return nil, errdefs.ToGRPC(err) 232 } 233 } 234 if err := p.Start(ctx); err != nil { 235 return nil, errdefs.ToGRPC(err) 236 } 237 state, err := p.State(ctx) 238 if err != nil { 239 return nil, errdefs.ToGRPC(err) 240 } 241 return &api.StartResponse{ 242 Pid: state.Pid, 243 }, nil 244 } 245 246 func (l *local) Delete(ctx context.Context, r *api.DeleteTaskRequest, _ ...grpc.CallOption) (*api.DeleteResponse, error) { 247 t, err := l.getTask(ctx, r.ContainerID) 248 if err != nil { 249 return nil, err 250 } 251 if err := l.monitor.Stop(t); err != nil { 252 return nil, err 253 } 254 exit, err := t.Delete(ctx) 255 if err != nil { 256 return nil, errdefs.ToGRPC(err) 257 } 258 return &api.DeleteResponse{ 259 ExitStatus: exit.Status, 260 ExitedAt: exit.Timestamp, 261 Pid: exit.Pid, 262 }, nil 263 } 264 265 func (l *local) DeleteProcess(ctx context.Context, r *api.DeleteProcessRequest, _ ...grpc.CallOption) (*api.DeleteResponse, error) { 266 t, err := l.getTask(ctx, r.ContainerID) 267 if err != nil { 268 return nil, err 269 } 270 process, err := t.Process(ctx, r.ExecID) 271 if err != nil { 272 return nil, errdefs.ToGRPC(err) 273 } 274 exit, err := process.Delete(ctx) 275 if err != nil { 276 return nil, errdefs.ToGRPC(err) 277 } 278 return &api.DeleteResponse{ 279 ID: r.ExecID, 280 ExitStatus: exit.Status, 281 ExitedAt: exit.Timestamp, 282 Pid: exit.Pid, 283 }, nil 284 } 285 286 func getProcessState(ctx context.Context, p runtime.Process) (*task.Process, error) { 287 ctx, cancel := timeout.WithContext(ctx, stateTimeout) 288 defer cancel() 289 290 state, err := p.State(ctx) 291 if err != nil { 292 if errdefs.IsNotFound(err) { 293 return nil, err 294 } 295 log.G(ctx).WithError(err).Errorf("get state for %s", p.ID()) 296 } 297 status := task.StatusUnknown 298 switch state.Status { 299 case runtime.CreatedStatus: 300 status = task.StatusCreated 301 case runtime.RunningStatus: 302 status = task.StatusRunning 303 case runtime.StoppedStatus: 304 status = task.StatusStopped 305 case runtime.PausedStatus: 306 status = task.StatusPaused 307 case runtime.PausingStatus: 308 status = task.StatusPausing 309 default: 310 log.G(ctx).WithField("status", state.Status).Warn("unknown status") 311 } 312 return &task.Process{ 313 ID: p.ID(), 314 Pid: state.Pid, 315 Status: status, 316 Stdin: state.Stdin, 317 Stdout: state.Stdout, 318 Stderr: state.Stderr, 319 Terminal: state.Terminal, 320 ExitStatus: state.ExitStatus, 321 ExitedAt: state.ExitedAt, 322 }, nil 323 } 324 325 func (l *local) Get(ctx context.Context, r *api.GetRequest, _ ...grpc.CallOption) (*api.GetResponse, error) { 326 task, err := l.getTask(ctx, r.ContainerID) 327 if err != nil { 328 return nil, err 329 } 330 p := runtime.Process(task) 331 if r.ExecID != "" { 332 if p, err = task.Process(ctx, r.ExecID); err != nil { 333 return nil, errdefs.ToGRPC(err) 334 } 335 } 336 t, err := getProcessState(ctx, p) 337 if err != nil { 338 return nil, errdefs.ToGRPC(err) 339 } 340 return &api.GetResponse{ 341 Process: t, 342 }, nil 343 } 344 345 func (l *local) List(ctx context.Context, r *api.ListTasksRequest, _ ...grpc.CallOption) (*api.ListTasksResponse, error) { 346 resp := &api.ListTasksResponse{} 347 for _, r := range l.allRuntimes() { 348 tasks, err := r.Tasks(ctx, false) 349 if err != nil { 350 return nil, errdefs.ToGRPC(err) 351 } 352 addTasks(ctx, resp, tasks) 353 } 354 return resp, nil 355 } 356 357 func addTasks(ctx context.Context, r *api.ListTasksResponse, tasks []runtime.Task) { 358 for _, t := range tasks { 359 tt, err := getProcessState(ctx, t) 360 if err != nil { 361 if !errdefs.IsNotFound(err) { // handle race with deletion 362 log.G(ctx).WithError(err).WithField("id", t.ID()).Error("converting task to protobuf") 363 } 364 continue 365 } 366 r.Tasks = append(r.Tasks, tt) 367 } 368 } 369 370 func (l *local) Pause(ctx context.Context, r *api.PauseTaskRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 371 t, err := l.getTask(ctx, r.ContainerID) 372 if err != nil { 373 return nil, err 374 } 375 err = t.Pause(ctx) 376 if err != nil { 377 return nil, errdefs.ToGRPC(err) 378 } 379 return empty, nil 380 } 381 382 func (l *local) Resume(ctx context.Context, r *api.ResumeTaskRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 383 t, err := l.getTask(ctx, r.ContainerID) 384 if err != nil { 385 return nil, err 386 } 387 err = t.Resume(ctx) 388 if err != nil { 389 return nil, errdefs.ToGRPC(err) 390 } 391 return empty, nil 392 } 393 394 func (l *local) Kill(ctx context.Context, r *api.KillRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 395 t, err := l.getTask(ctx, r.ContainerID) 396 if err != nil { 397 return nil, err 398 } 399 p := runtime.Process(t) 400 if r.ExecID != "" { 401 if p, err = t.Process(ctx, r.ExecID); err != nil { 402 return nil, errdefs.ToGRPC(err) 403 } 404 } 405 if err := p.Kill(ctx, r.Signal, r.All); err != nil { 406 return nil, errdefs.ToGRPC(err) 407 } 408 return empty, nil 409 } 410 411 func (l *local) ListPids(ctx context.Context, r *api.ListPidsRequest, _ ...grpc.CallOption) (*api.ListPidsResponse, error) { 412 t, err := l.getTask(ctx, r.ContainerID) 413 if err != nil { 414 return nil, err 415 } 416 processList, err := t.Pids(ctx) 417 if err != nil { 418 return nil, errdefs.ToGRPC(err) 419 } 420 var processes []*task.ProcessInfo 421 for _, p := range processList { 422 pInfo := task.ProcessInfo{ 423 Pid: p.Pid, 424 } 425 if p.Info != nil { 426 a, err := typeurl.MarshalAny(p.Info) 427 if err != nil { 428 return nil, errors.Wrapf(err, "failed to marshal process %d info", p.Pid) 429 } 430 pInfo.Info = a 431 } 432 processes = append(processes, &pInfo) 433 } 434 return &api.ListPidsResponse{ 435 Processes: processes, 436 }, nil 437 } 438 439 func (l *local) Exec(ctx context.Context, r *api.ExecProcessRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 440 if r.ExecID == "" { 441 return nil, status.Errorf(codes.InvalidArgument, "exec id cannot be empty") 442 } 443 t, err := l.getTask(ctx, r.ContainerID) 444 if err != nil { 445 return nil, err 446 } 447 if _, err := t.Exec(ctx, r.ExecID, runtime.ExecOpts{ 448 Spec: r.Spec, 449 IO: runtime.IO{ 450 Stdin: r.Stdin, 451 Stdout: r.Stdout, 452 Stderr: r.Stderr, 453 Terminal: r.Terminal, 454 }, 455 }); err != nil { 456 return nil, errdefs.ToGRPC(err) 457 } 458 return empty, nil 459 } 460 461 func (l *local) ResizePty(ctx context.Context, r *api.ResizePtyRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 462 t, err := l.getTask(ctx, r.ContainerID) 463 if err != nil { 464 return nil, err 465 } 466 p := runtime.Process(t) 467 if r.ExecID != "" { 468 if p, err = t.Process(ctx, r.ExecID); err != nil { 469 return nil, errdefs.ToGRPC(err) 470 } 471 } 472 if err := p.ResizePty(ctx, runtime.ConsoleSize{ 473 Width: r.Width, 474 Height: r.Height, 475 }); err != nil { 476 return nil, errdefs.ToGRPC(err) 477 } 478 return empty, nil 479 } 480 481 func (l *local) CloseIO(ctx context.Context, r *api.CloseIORequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 482 t, err := l.getTask(ctx, r.ContainerID) 483 if err != nil { 484 return nil, err 485 } 486 p := runtime.Process(t) 487 if r.ExecID != "" { 488 if p, err = t.Process(ctx, r.ExecID); err != nil { 489 return nil, errdefs.ToGRPC(err) 490 } 491 } 492 if r.Stdin { 493 if err := p.CloseIO(ctx); err != nil { 494 return nil, errdefs.ToGRPC(err) 495 } 496 } 497 return empty, nil 498 } 499 500 func (l *local) Checkpoint(ctx context.Context, r *api.CheckpointTaskRequest, _ ...grpc.CallOption) (*api.CheckpointTaskResponse, error) { 501 container, err := l.getContainer(ctx, r.ContainerID) 502 if err != nil { 503 return nil, err 504 } 505 t, err := l.getTaskFromContainer(ctx, container) 506 if err != nil { 507 return nil, err 508 } 509 image, err := getCheckpointPath(container.Runtime.Name, r.Options) 510 if err != nil { 511 return nil, err 512 } 513 checkpointImageExists := false 514 if image == "" { 515 checkpointImageExists = true 516 image, err = ioutil.TempDir(os.Getenv("XDG_RUNTIME_DIR"), "ctd-checkpoint") 517 if err != nil { 518 return nil, errdefs.ToGRPC(err) 519 } 520 defer os.RemoveAll(image) 521 } 522 if err := t.Checkpoint(ctx, image, r.Options); err != nil { 523 return nil, errdefs.ToGRPC(err) 524 } 525 // do not commit checkpoint image if checkpoint ImagePath is passed, 526 // return if checkpointImageExists is false 527 if !checkpointImageExists { 528 return &api.CheckpointTaskResponse{}, nil 529 } 530 // write checkpoint to the content store 531 tar := archive.Diff(ctx, "", image) 532 cp, err := l.writeContent(ctx, images.MediaTypeContainerd1Checkpoint, image, tar) 533 // close tar first after write 534 if err := tar.Close(); err != nil { 535 return nil, err 536 } 537 if err != nil { 538 return nil, err 539 } 540 // write the config to the content store 541 data, err := container.Spec.Marshal() 542 if err != nil { 543 return nil, err 544 } 545 spec := bytes.NewReader(data) 546 specD, err := l.writeContent(ctx, images.MediaTypeContainerd1CheckpointConfig, filepath.Join(image, "spec"), spec) 547 if err != nil { 548 return nil, errdefs.ToGRPC(err) 549 } 550 return &api.CheckpointTaskResponse{ 551 Descriptors: []*types.Descriptor{ 552 cp, 553 specD, 554 }, 555 }, nil 556 } 557 558 func (l *local) Update(ctx context.Context, r *api.UpdateTaskRequest, _ ...grpc.CallOption) (*ptypes.Empty, error) { 559 t, err := l.getTask(ctx, r.ContainerID) 560 if err != nil { 561 return nil, err 562 } 563 if err := t.Update(ctx, r.Resources); err != nil { 564 return nil, errdefs.ToGRPC(err) 565 } 566 return empty, nil 567 } 568 569 func (l *local) Metrics(ctx context.Context, r *api.MetricsRequest, _ ...grpc.CallOption) (*api.MetricsResponse, error) { 570 filter, err := filters.ParseAll(r.Filters...) 571 if err != nil { 572 return nil, err 573 } 574 var resp api.MetricsResponse 575 for _, r := range l.allRuntimes() { 576 tasks, err := r.Tasks(ctx, false) 577 if err != nil { 578 return nil, err 579 } 580 getTasksMetrics(ctx, filter, tasks, &resp) 581 } 582 return &resp, nil 583 } 584 585 func (l *local) Wait(ctx context.Context, r *api.WaitRequest, _ ...grpc.CallOption) (*api.WaitResponse, error) { 586 t, err := l.getTask(ctx, r.ContainerID) 587 if err != nil { 588 return nil, err 589 } 590 p := runtime.Process(t) 591 if r.ExecID != "" { 592 if p, err = t.Process(ctx, r.ExecID); err != nil { 593 return nil, errdefs.ToGRPC(err) 594 } 595 } 596 exit, err := p.Wait(ctx) 597 if err != nil { 598 return nil, errdefs.ToGRPC(err) 599 } 600 return &api.WaitResponse{ 601 ExitStatus: exit.Status, 602 ExitedAt: exit.Timestamp, 603 }, nil 604 } 605 606 func getTasksMetrics(ctx context.Context, filter filters.Filter, tasks []runtime.Task, r *api.MetricsResponse) { 607 for _, tk := range tasks { 608 if !filter.Match(filters.AdapterFunc(func(fieldpath []string) (string, bool) { 609 t := tk 610 switch fieldpath[0] { 611 case "id": 612 return t.ID(), true 613 case "namespace": 614 return t.Namespace(), true 615 case "runtime": 616 // return t.Info().Runtime, true 617 } 618 return "", false 619 })) { 620 continue 621 } 622 collected := time.Now() 623 stats, err := tk.Stats(ctx) 624 if err != nil { 625 if !errdefs.IsNotFound(err) { 626 log.G(ctx).WithError(err).Errorf("collecting metrics for %s", tk.ID()) 627 } 628 continue 629 } 630 r.Metrics = append(r.Metrics, &types.Metric{ 631 Timestamp: collected, 632 ID: tk.ID(), 633 Data: stats, 634 }) 635 } 636 } 637 638 func (l *local) writeContent(ctx context.Context, mediaType, ref string, r io.Reader) (*types.Descriptor, error) { 639 writer, err := l.store.Writer(ctx, content.WithRef(ref), content.WithDescriptor(ocispec.Descriptor{MediaType: mediaType})) 640 if err != nil { 641 return nil, err 642 } 643 defer writer.Close() 644 size, err := io.Copy(writer, r) 645 if err != nil { 646 return nil, err 647 } 648 if err := writer.Commit(ctx, 0, ""); err != nil { 649 return nil, err 650 } 651 return &types.Descriptor{ 652 MediaType: mediaType, 653 Digest: writer.Digest(), 654 Size_: size, 655 Annotations: make(map[string]string), 656 }, nil 657 } 658 659 func (l *local) getContainer(ctx context.Context, id string) (*containers.Container, error) { 660 var container containers.Container 661 container, err := l.containers.Get(ctx, id) 662 if err != nil { 663 return nil, errdefs.ToGRPC(err) 664 } 665 return &container, nil 666 } 667 668 func (l *local) getTask(ctx context.Context, id string) (runtime.Task, error) { 669 container, err := l.getContainer(ctx, id) 670 if err != nil { 671 return nil, err 672 } 673 return l.getTaskFromContainer(ctx, container) 674 } 675 676 func (l *local) getTaskFromContainer(ctx context.Context, container *containers.Container) (runtime.Task, error) { 677 runtime, err := l.getRuntime(container.Runtime.Name) 678 if err != nil { 679 return nil, errdefs.ToGRPCf(err, "runtime for task %s", container.Runtime.Name) 680 } 681 t, err := runtime.Get(ctx, container.ID) 682 if err != nil { 683 return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID) 684 } 685 return t, nil 686 } 687 688 func (l *local) getRuntime(name string) (runtime.PlatformRuntime, error) { 689 runtime, ok := l.runtimes[name] 690 if !ok { 691 // one runtime to rule them all 692 return l.v2Runtime, nil 693 } 694 return runtime, nil 695 } 696 697 func (l *local) allRuntimes() (o []runtime.PlatformRuntime) { 698 for _, r := range l.runtimes { 699 o = append(o, r) 700 } 701 o = append(o, l.v2Runtime) 702 return o 703 } 704 705 // getCheckpointPath only suitable for runc runtime now 706 func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) { 707 if option == nil { 708 return "", nil 709 } 710 711 var checkpointPath string 712 switch { 713 case checkRuntime(runtime, "io.containerd.runc"): 714 v, err := typeurl.UnmarshalAny(option) 715 if err != nil { 716 return "", err 717 } 718 opts, ok := v.(*options.CheckpointOptions) 719 if !ok { 720 return "", fmt.Errorf("invalid task checkpoint option for %s", runtime) 721 } 722 checkpointPath = opts.ImagePath 723 724 case runtime == plugin.RuntimeLinuxV1: 725 v, err := typeurl.UnmarshalAny(option) 726 if err != nil { 727 return "", err 728 } 729 opts, ok := v.(*runctypes.CheckpointOptions) 730 if !ok { 731 return "", fmt.Errorf("invalid task checkpoint option for %s", runtime) 732 } 733 checkpointPath = opts.ImagePath 734 } 735 736 return checkpointPath, nil 737 } 738 739 // getRestorePath only suitable for runc runtime now 740 func getRestorePath(runtime string, option *ptypes.Any) (string, error) { 741 if option == nil { 742 return "", nil 743 } 744 745 var restorePath string 746 switch { 747 case checkRuntime(runtime, "io.containerd.runc"): 748 v, err := typeurl.UnmarshalAny(option) 749 if err != nil { 750 return "", err 751 } 752 opts, ok := v.(*options.Options) 753 if !ok { 754 return "", fmt.Errorf("invalid task create option for %s", runtime) 755 } 756 restorePath = opts.CriuImagePath 757 case runtime == plugin.RuntimeLinuxV1: 758 v, err := typeurl.UnmarshalAny(option) 759 if err != nil { 760 return "", err 761 } 762 opts, ok := v.(*runctypes.CreateOptions) 763 if !ok { 764 return "", fmt.Errorf("invalid task create option for %s", runtime) 765 } 766 restorePath = opts.CriuImagePath 767 } 768 769 return restorePath, nil 770 } 771 772 // checkRuntime returns true if the current runtime matches the expected 773 // runtime. Providing various parts of the runtime schema will match those 774 // parts of the expected runtime 775 func checkRuntime(current, expected string) bool { 776 cp := strings.Split(current, ".") 777 l := len(cp) 778 for i, p := range strings.Split(expected, ".") { 779 if i > l { 780 return false 781 } 782 if p != cp[i] { 783 return false 784 } 785 } 786 return true 787 }