github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/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  }