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