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  }