Seperate shim manager and task service
Create new shim manager interface and deprecate older shim manager interface. Signed-off-by: Derek McGowan <derek@mcg.dev>
This commit is contained in:
parent
6835a94707
commit
04e57d71b2
@ -1,5 +1,3 @@
|
|||||||
// +build linux
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright The containerd Authors.
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
@ -16,7 +14,7 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package service
|
package manager
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -30,6 +28,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
cgroupsv2 "github.com/containerd/cgroups/v2"
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
"github.com/containerd/containerd/pkg/process"
|
"github.com/containerd/containerd/pkg/process"
|
||||||
@ -37,22 +36,20 @@ import (
|
|||||||
"github.com/containerd/containerd/runtime/v2/runc"
|
"github.com/containerd/containerd/runtime/v2/runc"
|
||||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||||
"github.com/containerd/containerd/runtime/v2/shim"
|
"github.com/containerd/containerd/runtime/v2/shim"
|
||||||
taskAPI "github.com/containerd/containerd/runtime/v2/task"
|
|
||||||
runcC "github.com/containerd/go-runc"
|
runcC "github.com/containerd/go-runc"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
"github.com/gogo/protobuf/proto"
|
"github.com/gogo/protobuf/proto"
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
exec "golang.org/x/sys/execabs"
|
exec "golang.org/x/sys/execabs"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewShimManager returns an implementation of the shim manager
|
// NewShimManager returns an implementation of the shim manager
|
||||||
// using runc
|
// using runc
|
||||||
func NewShimManager(id string) shim.Shim {
|
func NewShimManager(name string) shim.Manager {
|
||||||
return &manager{
|
return &manager{
|
||||||
id: id,
|
name: name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +66,7 @@ type spec struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type manager struct {
|
type manager struct {
|
||||||
id string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCommand(ctx context.Context, id, containerdBinary, containerdAddress, containerdTTRPCAddress string) (*exec.Cmd, error) {
|
func newCommand(ctx context.Context, id, containerdBinary, containerdAddress, containerdTTRPCAddress string) (*exec.Cmd, error) {
|
||||||
@ -112,12 +109,16 @@ func readSpec() (*spec, error) {
|
|||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (manager) StartShim(ctx context.Context, opts shim.StartOpts) (_ string, retErr error) {
|
func (m manager) Name() string {
|
||||||
cmd, err := newCommand(ctx, opts.ID, opts.ContainerdBinary, opts.Address, opts.TTRPCAddress)
|
return m.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager) Start(ctx context.Context, id string, opts shim.StartOpts) (_ string, retErr error) {
|
||||||
|
cmd, err := newCommand(ctx, id, opts.ContainerdBinary, opts.Address, opts.TTRPCAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
grouping := opts.ID
|
grouping := id
|
||||||
spec, err := readSpec()
|
spec, err := readSpec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -236,24 +237,24 @@ func (manager) StartShim(ctx context.Context, opts shim.StartOpts) (_ string, re
|
|||||||
return address, nil
|
return address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m manager) Cleanup(ctx context.Context) (*taskAPI.DeleteResponse, error) {
|
func (manager) Stop(ctx context.Context, id string) (shim.StopStatus, error) {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return shim.StopStatus{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
path := filepath.Join(filepath.Dir(cwd), m.id)
|
path := filepath.Join(filepath.Dir(cwd), id)
|
||||||
ns, err := namespaces.NamespaceRequired(ctx)
|
ns, err := namespaces.NamespaceRequired(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return shim.StopStatus{}, err
|
||||||
}
|
}
|
||||||
runtime, err := runc.ReadRuntime(path)
|
runtime, err := runc.ReadRuntime(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return shim.StopStatus{}, err
|
||||||
}
|
}
|
||||||
opts, err := runc.ReadOptions(path)
|
opts, err := runc.ReadOptions(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return shim.StopStatus{}, err
|
||||||
}
|
}
|
||||||
root := process.RuncRoot
|
root := process.RuncRoot
|
||||||
if opts != nil && opts.Root != "" {
|
if opts != nil && opts.Root != "" {
|
||||||
@ -261,16 +262,16 @@ func (m manager) Cleanup(ctx context.Context) (*taskAPI.DeleteResponse, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := process.NewRunc(root, path, ns, runtime, "", false)
|
r := process.NewRunc(root, path, ns, runtime, "", false)
|
||||||
if err := r.Delete(ctx, m.id, &runcC.DeleteOpts{
|
if err := r.Delete(ctx, id, &runcC.DeleteOpts{
|
||||||
Force: true,
|
Force: true,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
logrus.WithError(err).Warn("failed to remove runc container")
|
log.G(ctx).WithError(err).Warn("failed to remove runc container")
|
||||||
}
|
}
|
||||||
if err := mount.UnmountAll(filepath.Join(path, "rootfs"), 0); err != nil {
|
if err := mount.UnmountAll(filepath.Join(path, "rootfs"), 0); err != nil {
|
||||||
logrus.WithError(err).Warn("failed to cleanup rootfs mount")
|
log.G(ctx).WithError(err).Warn("failed to cleanup rootfs mount")
|
||||||
}
|
}
|
||||||
return &taskAPI.DeleteResponse{
|
return shim.StopStatus{
|
||||||
ExitedAt: time.Now(),
|
ExitedAt: time.Now(),
|
||||||
ExitStatus: 128 + uint32(unix.SIGKILL),
|
ExitStatus: 128 + int(unix.SIGKILL),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
619
runtime/v2/runc/tasks/service.go
Normal file
619
runtime/v2/runc/tasks/service.go
Normal file
@ -0,0 +1,619 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tasks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/containerd/cgroups"
|
||||||
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
|
"github.com/containerd/containerd/api/types/task"
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/pkg/oom"
|
||||||
|
oomv1 "github.com/containerd/containerd/pkg/oom/v1"
|
||||||
|
oomv2 "github.com/containerd/containerd/pkg/oom/v2"
|
||||||
|
"github.com/containerd/containerd/pkg/process"
|
||||||
|
"github.com/containerd/containerd/pkg/shutdown"
|
||||||
|
"github.com/containerd/containerd/pkg/stdio"
|
||||||
|
"github.com/containerd/containerd/pkg/userns"
|
||||||
|
"github.com/containerd/containerd/runtime/v2/runc"
|
||||||
|
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||||
|
"github.com/containerd/containerd/runtime/v2/shim"
|
||||||
|
shimapi "github.com/containerd/containerd/runtime/v2/task"
|
||||||
|
taskAPI "github.com/containerd/containerd/runtime/v2/task"
|
||||||
|
"github.com/containerd/containerd/sys/reaper"
|
||||||
|
runcC "github.com/containerd/go-runc"
|
||||||
|
"github.com/containerd/ttrpc"
|
||||||
|
"github.com/containerd/typeurl"
|
||||||
|
ptypes "github.com/gogo/protobuf/types"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ = (taskAPI.TaskService)(&service{})
|
||||||
|
empty = &ptypes.Empty{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTaskService creates a new instance of a task service
|
||||||
|
func NewTaskService(ctx context.Context, publisher shim.Publisher, sd shutdown.Service) (taskAPI.TaskService, error) {
|
||||||
|
var (
|
||||||
|
ep oom.Watcher
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
ep, err = oomv2.New(publisher)
|
||||||
|
} else {
|
||||||
|
ep, err = oomv1.New(publisher)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
go ep.Run(ctx)
|
||||||
|
s := &service{
|
||||||
|
context: ctx,
|
||||||
|
events: make(chan interface{}, 128),
|
||||||
|
ec: reaper.Default.Subscribe(),
|
||||||
|
ep: ep,
|
||||||
|
shutdown: sd,
|
||||||
|
containers: make(map[string]*runc.Container),
|
||||||
|
}
|
||||||
|
go s.processExits()
|
||||||
|
runcC.Monitor = reaper.Default
|
||||||
|
if err := s.initPlatform(); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed to initialized platform behavior")
|
||||||
|
}
|
||||||
|
go s.forward(ctx, publisher)
|
||||||
|
sd.RegisterCallback(func(context.Context) error {
|
||||||
|
close(s.events)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if address, err := shim.ReadAddress("address"); err == nil {
|
||||||
|
sd.RegisterCallback(func(context.Context) error {
|
||||||
|
return shim.RemoveSocket(address)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// service is the shim implementation of a remote shim over GRPC
|
||||||
|
type service struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
eventSendMu sync.Mutex
|
||||||
|
|
||||||
|
context context.Context
|
||||||
|
events chan interface{}
|
||||||
|
platform stdio.Platform
|
||||||
|
ec chan runcC.Exit
|
||||||
|
ep oom.Watcher
|
||||||
|
|
||||||
|
containers map[string]*runc.Container
|
||||||
|
|
||||||
|
shutdown shutdown.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new initial process and container with the underlying OCI runtime
|
||||||
|
func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (_ *taskAPI.CreateTaskResponse, err error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
container, err := runc.NewContainer(ctx, s.platform, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.containers[r.ID] = container
|
||||||
|
|
||||||
|
s.send(&eventstypes.TaskCreate{
|
||||||
|
ContainerID: r.ID,
|
||||||
|
Bundle: r.Bundle,
|
||||||
|
Rootfs: r.Rootfs,
|
||||||
|
IO: &eventstypes.TaskIO{
|
||||||
|
Stdin: r.Stdin,
|
||||||
|
Stdout: r.Stdout,
|
||||||
|
Stderr: r.Stderr,
|
||||||
|
Terminal: r.Terminal,
|
||||||
|
},
|
||||||
|
Checkpoint: r.Checkpoint,
|
||||||
|
Pid: uint32(container.Pid()),
|
||||||
|
})
|
||||||
|
|
||||||
|
return &taskAPI.CreateTaskResponse{
|
||||||
|
Pid: uint32(container.Pid()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) RegisterTTRPC(server *ttrpc.Server) error {
|
||||||
|
shimapi.RegisterTaskService(server, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a process
|
||||||
|
func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.StartResponse, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// hold the send lock so that the start events are sent before any exit events in the error case
|
||||||
|
s.eventSendMu.Lock()
|
||||||
|
p, err := container.Start(ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
s.eventSendMu.Unlock()
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch r.ExecID {
|
||||||
|
case "":
|
||||||
|
switch cg := container.Cgroup().(type) {
|
||||||
|
case cgroups.Cgroup:
|
||||||
|
if err := s.ep.Add(container.ID, cg); err != nil {
|
||||||
|
logrus.WithError(err).Error("add cg to OOM monitor")
|
||||||
|
}
|
||||||
|
case *cgroupsv2.Manager:
|
||||||
|
allControllers, err := cg.RootControllers()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("failed to get root controllers")
|
||||||
|
} else {
|
||||||
|
if err := cg.ToggleControllers(allControllers, cgroupsv2.Enable); err != nil {
|
||||||
|
if userns.RunningInUserNS() {
|
||||||
|
logrus.WithError(err).Debugf("failed to enable controllers (%v)", allControllers)
|
||||||
|
} else {
|
||||||
|
logrus.WithError(err).Errorf("failed to enable controllers (%v)", allControllers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.ep.Add(container.ID, cg); err != nil {
|
||||||
|
logrus.WithError(err).Error("add cg to OOM monitor")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.send(&eventstypes.TaskStart{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
Pid: uint32(p.Pid()),
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
s.send(&eventstypes.TaskExecStarted{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
ExecID: r.ExecID,
|
||||||
|
Pid: uint32(p.Pid()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
s.eventSendMu.Unlock()
|
||||||
|
return &taskAPI.StartResponse{
|
||||||
|
Pid: uint32(p.Pid()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the initial process and container
|
||||||
|
func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, err := container.Delete(ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
// if we deleted an init task, send the task delete event
|
||||||
|
if r.ExecID == "" {
|
||||||
|
s.mu.Lock()
|
||||||
|
delete(s.containers, r.ID)
|
||||||
|
s.mu.Unlock()
|
||||||
|
s.send(&eventstypes.TaskDelete{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
Pid: uint32(p.Pid()),
|
||||||
|
ExitStatus: uint32(p.ExitStatus()),
|
||||||
|
ExitedAt: p.ExitedAt(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &taskAPI.DeleteResponse{
|
||||||
|
ExitStatus: uint32(p.ExitStatus()),
|
||||||
|
ExitedAt: p.ExitedAt(),
|
||||||
|
Pid: uint32(p.Pid()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exec an additional process inside the container
|
||||||
|
func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ok, cancel := container.ReserveProcess(r.ExecID)
|
||||||
|
if !ok {
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ExecID)
|
||||||
|
}
|
||||||
|
process, err := container.Exec(ctx, r)
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.send(&eventstypes.TaskExecAdded{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
ExecID: process.ID(),
|
||||||
|
})
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResizePty of a process
|
||||||
|
func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.ResizePty(ctx, r); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// State returns runtime state information for a process
|
||||||
|
func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.StateResponse, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, err := container.Process(r.ExecID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
st, err := p.Status(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
status := task.StatusUnknown
|
||||||
|
switch st {
|
||||||
|
case "created":
|
||||||
|
status = task.StatusCreated
|
||||||
|
case "running":
|
||||||
|
status = task.StatusRunning
|
||||||
|
case "stopped":
|
||||||
|
status = task.StatusStopped
|
||||||
|
case "paused":
|
||||||
|
status = task.StatusPaused
|
||||||
|
case "pausing":
|
||||||
|
status = task.StatusPausing
|
||||||
|
}
|
||||||
|
sio := p.Stdio()
|
||||||
|
return &taskAPI.StateResponse{
|
||||||
|
ID: p.ID(),
|
||||||
|
Bundle: container.Bundle,
|
||||||
|
Pid: uint32(p.Pid()),
|
||||||
|
Status: status,
|
||||||
|
Stdin: sio.Stdin,
|
||||||
|
Stdout: sio.Stdout,
|
||||||
|
Stderr: sio.Stderr,
|
||||||
|
Terminal: sio.Terminal,
|
||||||
|
ExitStatus: uint32(p.ExitStatus()),
|
||||||
|
ExitedAt: p.ExitedAt(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause the container
|
||||||
|
func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.Pause(ctx); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
s.send(&eventstypes.TaskPaused{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
})
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume the container
|
||||||
|
func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.Resume(ctx); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
s.send(&eventstypes.TaskResumed{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
})
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill a process with the provided signal
|
||||||
|
func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.Kill(ctx, r); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pids returns all pids inside the container
|
||||||
|
func (s *service) Pids(ctx context.Context, r *taskAPI.PidsRequest) (*taskAPI.PidsResponse, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pids, err := s.getContainerPids(ctx, r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
var processes []*task.ProcessInfo
|
||||||
|
for _, pid := range pids {
|
||||||
|
pInfo := task.ProcessInfo{
|
||||||
|
Pid: pid,
|
||||||
|
}
|
||||||
|
for _, p := range container.ExecdProcesses() {
|
||||||
|
if p.Pid() == int(pid) {
|
||||||
|
d := &options.ProcessDetails{
|
||||||
|
ExecID: p.ID(),
|
||||||
|
}
|
||||||
|
a, err := typeurl.MarshalAny(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to marshal process %d info", pid)
|
||||||
|
}
|
||||||
|
pInfo.Info = a
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processes = append(processes, &pInfo)
|
||||||
|
}
|
||||||
|
return &taskAPI.PidsResponse{
|
||||||
|
Processes: processes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseIO of a process
|
||||||
|
func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.CloseIO(ctx, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checkpoint the container
|
||||||
|
func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.Checkpoint(ctx, r); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update a running container
|
||||||
|
func (s *service) Update(ctx context.Context, r *taskAPI.UpdateTaskRequest) (*ptypes.Empty, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := container.Update(ctx, r); err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a process to exit
|
||||||
|
func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.WaitResponse, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, err := container.Process(r.ExecID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
p.Wait()
|
||||||
|
|
||||||
|
return &taskAPI.WaitResponse{
|
||||||
|
ExitStatus: uint32(p.ExitStatus()),
|
||||||
|
ExitedAt: p.ExitedAt(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect returns shim information such as the shim's pid
|
||||||
|
func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*taskAPI.ConnectResponse, error) {
|
||||||
|
var pid int
|
||||||
|
if container, err := s.getContainer(r.ID); err == nil {
|
||||||
|
pid = container.Pid()
|
||||||
|
}
|
||||||
|
return &taskAPI.ConnectResponse{
|
||||||
|
ShimPid: uint32(os.Getpid()),
|
||||||
|
TaskPid: uint32(pid),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*ptypes.Empty, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
// return out if the shim is still servicing containers
|
||||||
|
if len(s.containers) > 0 {
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// please make sure that temporary resource has been cleanup or registered
|
||||||
|
// for cleanup before calling shutdown
|
||||||
|
s.shutdown.Shutdown()
|
||||||
|
|
||||||
|
return empty, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
||||||
|
container, err := s.getContainer(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cgx := container.Cgroup()
|
||||||
|
if cgx == nil {
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
||||||
|
}
|
||||||
|
var statsx interface{}
|
||||||
|
switch cg := cgx.(type) {
|
||||||
|
case cgroups.Cgroup:
|
||||||
|
stats, err := cg.Stat(cgroups.IgnoreNotExist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statsx = stats
|
||||||
|
case *cgroupsv2.Manager:
|
||||||
|
stats, err := cg.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statsx = stats
|
||||||
|
default:
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotImplemented, "unsupported cgroup type %T", cg)
|
||||||
|
}
|
||||||
|
data, err := typeurl.MarshalAny(statsx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &taskAPI.StatsResponse{
|
||||||
|
Stats: data,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) processExits() {
|
||||||
|
for e := range s.ec {
|
||||||
|
s.checkProcesses(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) send(evt interface{}) {
|
||||||
|
s.events <- evt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) sendL(evt interface{}) {
|
||||||
|
s.eventSendMu.Lock()
|
||||||
|
s.events <- evt
|
||||||
|
s.eventSendMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) checkProcesses(e runcC.Exit) {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
for _, container := range s.containers {
|
||||||
|
if !container.HasPid(e.Pid) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range container.All() {
|
||||||
|
if p.Pid() != e.Pid {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ip, ok := p.(*process.Init); ok {
|
||||||
|
// Ensure all children are killed
|
||||||
|
if runc.ShouldKillAllOnExit(s.context, container.Bundle) {
|
||||||
|
if err := ip.KillAll(s.context); err != nil {
|
||||||
|
logrus.WithError(err).WithField("id", ip.ID()).
|
||||||
|
Error("failed to kill init's children")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetExited(e.Status)
|
||||||
|
s.sendL(&eventstypes.TaskExit{
|
||||||
|
ContainerID: container.ID,
|
||||||
|
ID: p.ID(),
|
||||||
|
Pid: uint32(e.Pid),
|
||||||
|
ExitStatus: uint32(e.Status),
|
||||||
|
ExitedAt: p.ExitedAt(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
|
||||||
|
container, err := s.getContainer(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p, err := container.Process("")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errdefs.ToGRPC(err)
|
||||||
|
}
|
||||||
|
ps, err := p.(*process.Init).Runtime().Ps(ctx, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pids := make([]uint32, 0, len(ps))
|
||||||
|
for _, pid := range ps {
|
||||||
|
pids = append(pids, uint32(pid))
|
||||||
|
}
|
||||||
|
return pids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) forward(ctx context.Context, publisher shim.Publisher) {
|
||||||
|
ns, _ := namespaces.Namespace(ctx)
|
||||||
|
ctx = namespaces.WithNamespace(context.Background(), ns)
|
||||||
|
for e := range s.events {
|
||||||
|
err := publisher.Publish(ctx, runc.GetTopic(e), e)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("post event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
publisher.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *service) getContainer(id string) (*runc.Container, error) {
|
||||||
|
s.mu.Lock()
|
||||||
|
container := s.containers[id]
|
||||||
|
s.mu.Unlock()
|
||||||
|
if container == nil {
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "container not created")
|
||||||
|
}
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize a single epoll fd to manage our consoles. `initPlatform` should
|
||||||
|
// only be called once.
|
||||||
|
func (s *service) initPlatform() error {
|
||||||
|
if s.platform != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p, err := runc.NewPlatform()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.platform = p
|
||||||
|
s.shutdown.RegisterCallback(func(context.Context) error { return s.platform.Close() })
|
||||||
|
return nil
|
||||||
|
}
|
@ -21,627 +21,59 @@ package v2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
|
||||||
cgroupsv2 "github.com/containerd/cgroups/v2"
|
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
|
||||||
"github.com/containerd/containerd/api/types/task"
|
|
||||||
"github.com/containerd/containerd/errdefs"
|
|
||||||
"github.com/containerd/containerd/namespaces"
|
|
||||||
"github.com/containerd/containerd/pkg/oom"
|
|
||||||
oomv1 "github.com/containerd/containerd/pkg/oom/v1"
|
|
||||||
oomv2 "github.com/containerd/containerd/pkg/oom/v2"
|
|
||||||
"github.com/containerd/containerd/pkg/process"
|
|
||||||
"github.com/containerd/containerd/pkg/shutdown"
|
"github.com/containerd/containerd/pkg/shutdown"
|
||||||
"github.com/containerd/containerd/pkg/stdio"
|
"github.com/containerd/containerd/runtime/v2/runc/manager"
|
||||||
"github.com/containerd/containerd/pkg/userns"
|
"github.com/containerd/containerd/runtime/v2/runc/tasks"
|
||||||
"github.com/containerd/containerd/plugin"
|
|
||||||
"github.com/containerd/containerd/runtime/v2/runc"
|
|
||||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
|
||||||
runcservice "github.com/containerd/containerd/runtime/v2/runc/service"
|
|
||||||
"github.com/containerd/containerd/runtime/v2/shim"
|
"github.com/containerd/containerd/runtime/v2/shim"
|
||||||
shimapi "github.com/containerd/containerd/runtime/v2/task"
|
shimapi "github.com/containerd/containerd/runtime/v2/task"
|
||||||
taskAPI "github.com/containerd/containerd/runtime/v2/task"
|
|
||||||
"github.com/containerd/containerd/sys/reaper"
|
|
||||||
runcC "github.com/containerd/go-runc"
|
|
||||||
"github.com/containerd/ttrpc"
|
|
||||||
"github.com/containerd/typeurl"
|
|
||||||
ptypes "github.com/gogo/protobuf/types"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
// TODO(2.0): Remove this package
|
||||||
_ = (taskAPI.TaskService)(&service{})
|
|
||||||
empty = &ptypes.Empty{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// New returns a new shim service that can be used via GRPC
|
type shimTaskManager struct {
|
||||||
// TODO(2.0): Remove this function, rely on plugin registration
|
shimapi.TaskService
|
||||||
func New(_ context.Context, id string, _ shim.Publisher, _ func()) (shim.Shim, error) {
|
id string
|
||||||
plugin.Register(&plugin.Registration{
|
manager shim.Manager
|
||||||
Type: plugin.TTRPCPlugin,
|
|
||||||
ID: "task",
|
|
||||||
Requires: []plugin.Type{
|
|
||||||
plugin.EventPlugin,
|
|
||||||
plugin.InternalPlugin,
|
|
||||||
},
|
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
|
||||||
pp, err := ic.GetByID(plugin.EventPlugin, "publisher")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ss, err := ic.GetByID(plugin.InternalPlugin, "shutdown")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewTaskService(ic.Context, pp.(shim.Publisher), ss.(shutdown.Service))
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return runcservice.NewShimManager(id), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTaskService creates a new instance of a task service
|
func (stm *shimTaskManager) Cleanup(ctx context.Context) (*shimapi.DeleteResponse, error) {
|
||||||
func NewTaskService(ctx context.Context, publisher shim.Publisher, sd shutdown.Service) (taskAPI.TaskService, error) {
|
ss, err := stm.manager.Stop(ctx, stm.id)
|
||||||
var (
|
|
||||||
ep oom.Watcher
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if cgroups.Mode() == cgroups.Unified {
|
|
||||||
ep, err = oomv2.New(publisher)
|
|
||||||
} else {
|
|
||||||
ep, err = oomv1.New(publisher)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
go ep.Run(ctx)
|
return &shimapi.DeleteResponse{
|
||||||
s := &service{
|
Pid: uint32(ss.Pid),
|
||||||
context: ctx,
|
ExitStatus: uint32(ss.ExitStatus),
|
||||||
events: make(chan interface{}, 128),
|
ExitedAt: ss.ExitedAt,
|
||||||
ec: reaper.Default.Subscribe(),
|
|
||||||
ep: ep,
|
|
||||||
shutdown: sd,
|
|
||||||
containers: make(map[string]*runc.Container),
|
|
||||||
}
|
|
||||||
go s.processExits()
|
|
||||||
runcC.Monitor = reaper.Default
|
|
||||||
if err := s.initPlatform(); err != nil {
|
|
||||||
return nil, errors.Wrap(err, "failed to initialized platform behavior")
|
|
||||||
}
|
|
||||||
go s.forward(ctx, publisher)
|
|
||||||
sd.RegisterCallback(func(context.Context) error {
|
|
||||||
close(s.events)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if address, err := shim.ReadAddress("address"); err == nil {
|
|
||||||
sd.RegisterCallback(func(context.Context) error {
|
|
||||||
return shim.RemoveSocket(address)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// service is the shim implementation of a remote shim over GRPC
|
|
||||||
type service struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
eventSendMu sync.Mutex
|
|
||||||
|
|
||||||
context context.Context
|
|
||||||
events chan interface{}
|
|
||||||
platform stdio.Platform
|
|
||||||
ec chan runcC.Exit
|
|
||||||
ep oom.Watcher
|
|
||||||
|
|
||||||
containers map[string]*runc.Container
|
|
||||||
|
|
||||||
shutdown shutdown.Service
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new initial process and container with the underlying OCI runtime
|
|
||||||
func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (_ *taskAPI.CreateTaskResponse, err error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
|
|
||||||
container, err := runc.NewContainer(ctx, s.platform, r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.containers[r.ID] = container
|
|
||||||
|
|
||||||
s.send(&eventstypes.TaskCreate{
|
|
||||||
ContainerID: r.ID,
|
|
||||||
Bundle: r.Bundle,
|
|
||||||
Rootfs: r.Rootfs,
|
|
||||||
IO: &eventstypes.TaskIO{
|
|
||||||
Stdin: r.Stdin,
|
|
||||||
Stdout: r.Stdout,
|
|
||||||
Stderr: r.Stderr,
|
|
||||||
Terminal: r.Terminal,
|
|
||||||
},
|
|
||||||
Checkpoint: r.Checkpoint,
|
|
||||||
Pid: uint32(container.Pid()),
|
|
||||||
})
|
|
||||||
|
|
||||||
return &taskAPI.CreateTaskResponse{
|
|
||||||
Pid: uint32(container.Pid()),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) RegisterTTRPC(server *ttrpc.Server) error {
|
func (stm *shimTaskManager) StartShim(ctx context.Context, opts shim.StartOpts) (string, error) {
|
||||||
shimapi.RegisterTaskService(server, s)
|
return stm.manager.Start(ctx, opts.ID, opts)
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a process
|
// New returns a new shim service that can be used for
|
||||||
func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.StartResponse, error) {
|
// - serving the task service over grpc/ttrpc
|
||||||
container, err := s.getContainer(r.ID)
|
// - shim management
|
||||||
if err != nil {
|
// This function is deprecated in favor direct creation
|
||||||
return nil, err
|
// of shim manager and registering task service via plugins.
|
||||||
}
|
func New(ctx context.Context, id string, publisher shim.Publisher, fn func()) (shim.Shim, error) {
|
||||||
|
sd, ok := ctx.(shutdown.Service)
|
||||||
// hold the send lock so that the start events are sent before any exit events in the error case
|
|
||||||
s.eventSendMu.Lock()
|
|
||||||
p, err := container.Start(ctx, r)
|
|
||||||
if err != nil {
|
|
||||||
s.eventSendMu.Unlock()
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch r.ExecID {
|
|
||||||
case "":
|
|
||||||
switch cg := container.Cgroup().(type) {
|
|
||||||
case cgroups.Cgroup:
|
|
||||||
if err := s.ep.Add(container.ID, cg); err != nil {
|
|
||||||
logrus.WithError(err).Error("add cg to OOM monitor")
|
|
||||||
}
|
|
||||||
case *cgroupsv2.Manager:
|
|
||||||
allControllers, err := cg.RootControllers()
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Error("failed to get root controllers")
|
|
||||||
} else {
|
|
||||||
if err := cg.ToggleControllers(allControllers, cgroupsv2.Enable); err != nil {
|
|
||||||
if userns.RunningInUserNS() {
|
|
||||||
logrus.WithError(err).Debugf("failed to enable controllers (%v)", allControllers)
|
|
||||||
} else {
|
|
||||||
logrus.WithError(err).Errorf("failed to enable controllers (%v)", allControllers)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := s.ep.Add(container.ID, cg); err != nil {
|
|
||||||
logrus.WithError(err).Error("add cg to OOM monitor")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.send(&eventstypes.TaskStart{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
Pid: uint32(p.Pid()),
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
s.send(&eventstypes.TaskExecStarted{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
ExecID: r.ExecID,
|
|
||||||
Pid: uint32(p.Pid()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
s.eventSendMu.Unlock()
|
|
||||||
return &taskAPI.StartResponse{
|
|
||||||
Pid: uint32(p.Pid()),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the initial process and container
|
|
||||||
func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p, err := container.Delete(ctx, r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
// if we deleted an init task, send the task delete event
|
|
||||||
if r.ExecID == "" {
|
|
||||||
s.mu.Lock()
|
|
||||||
delete(s.containers, r.ID)
|
|
||||||
s.mu.Unlock()
|
|
||||||
s.send(&eventstypes.TaskDelete{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
Pid: uint32(p.Pid()),
|
|
||||||
ExitStatus: uint32(p.ExitStatus()),
|
|
||||||
ExitedAt: p.ExitedAt(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return &taskAPI.DeleteResponse{
|
|
||||||
ExitStatus: uint32(p.ExitStatus()),
|
|
||||||
ExitedAt: p.ExitedAt(),
|
|
||||||
Pid: uint32(p.Pid()),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exec an additional process inside the container
|
|
||||||
func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ok, cancel := container.ReserveProcess(r.ExecID)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ExecID)
|
ctx, sd = shutdown.WithShutdown(ctx)
|
||||||
|
sd.RegisterCallback(func(context.Context) error {
|
||||||
|
fn()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
process, err := container.Exec(ctx, r)
|
ts, err := tasks.NewTaskService(ctx, publisher, sd)
|
||||||
if err != nil {
|
|
||||||
cancel()
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.send(&eventstypes.TaskExecAdded{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
ExecID: process.ID(),
|
|
||||||
})
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResizePty of a process
|
|
||||||
func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := container.ResizePty(ctx, r); err != nil {
|
return &shimTaskManager{
|
||||||
return nil, errdefs.ToGRPC(err)
|
TaskService: ts,
|
||||||
}
|
id: id,
|
||||||
return empty, nil
|
manager: manager.NewShimManager("runc"),
|
||||||
}
|
|
||||||
|
|
||||||
// State returns runtime state information for a process
|
|
||||||
func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.StateResponse, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p, err := container.Process(r.ExecID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
st, err := p.Status(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
status := task.StatusUnknown
|
|
||||||
switch st {
|
|
||||||
case "created":
|
|
||||||
status = task.StatusCreated
|
|
||||||
case "running":
|
|
||||||
status = task.StatusRunning
|
|
||||||
case "stopped":
|
|
||||||
status = task.StatusStopped
|
|
||||||
case "paused":
|
|
||||||
status = task.StatusPaused
|
|
||||||
case "pausing":
|
|
||||||
status = task.StatusPausing
|
|
||||||
}
|
|
||||||
sio := p.Stdio()
|
|
||||||
return &taskAPI.StateResponse{
|
|
||||||
ID: p.ID(),
|
|
||||||
Bundle: container.Bundle,
|
|
||||||
Pid: uint32(p.Pid()),
|
|
||||||
Status: status,
|
|
||||||
Stdin: sio.Stdin,
|
|
||||||
Stdout: sio.Stdout,
|
|
||||||
Stderr: sio.Stderr,
|
|
||||||
Terminal: sio.Terminal,
|
|
||||||
ExitStatus: uint32(p.ExitStatus()),
|
|
||||||
ExitedAt: p.ExitedAt(),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause the container
|
|
||||||
func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Pause(ctx); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
s.send(&eventstypes.TaskPaused{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
})
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume the container
|
|
||||||
func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Resume(ctx); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
s.send(&eventstypes.TaskResumed{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
})
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kill a process with the provided signal
|
|
||||||
func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Kill(ctx, r); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pids returns all pids inside the container
|
|
||||||
func (s *service) Pids(ctx context.Context, r *taskAPI.PidsRequest) (*taskAPI.PidsResponse, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pids, err := s.getContainerPids(ctx, r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
var processes []*task.ProcessInfo
|
|
||||||
for _, pid := range pids {
|
|
||||||
pInfo := task.ProcessInfo{
|
|
||||||
Pid: pid,
|
|
||||||
}
|
|
||||||
for _, p := range container.ExecdProcesses() {
|
|
||||||
if p.Pid() == int(pid) {
|
|
||||||
d := &options.ProcessDetails{
|
|
||||||
ExecID: p.ID(),
|
|
||||||
}
|
|
||||||
a, err := typeurl.MarshalAny(d)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, "failed to marshal process %d info", pid)
|
|
||||||
}
|
|
||||||
pInfo.Info = a
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processes = append(processes, &pInfo)
|
|
||||||
}
|
|
||||||
return &taskAPI.PidsResponse{
|
|
||||||
Processes: processes,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloseIO of a process
|
|
||||||
func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.CloseIO(ctx, r); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checkpoint the container
|
|
||||||
func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Checkpoint(ctx, r); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update a running container
|
|
||||||
func (s *service) Update(ctx context.Context, r *taskAPI.UpdateTaskRequest) (*ptypes.Empty, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Update(ctx, r); err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for a process to exit
|
|
||||||
func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.WaitResponse, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p, err := container.Process(r.ExecID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
p.Wait()
|
|
||||||
|
|
||||||
return &taskAPI.WaitResponse{
|
|
||||||
ExitStatus: uint32(p.ExitStatus()),
|
|
||||||
ExitedAt: p.ExitedAt(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect returns shim information such as the shim's pid
|
|
||||||
func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*taskAPI.ConnectResponse, error) {
|
|
||||||
var pid int
|
|
||||||
if container, err := s.getContainer(r.ID); err == nil {
|
|
||||||
pid = container.Pid()
|
|
||||||
}
|
|
||||||
return &taskAPI.ConnectResponse{
|
|
||||||
ShimPid: uint32(os.Getpid()),
|
|
||||||
TaskPid: uint32(pid),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*ptypes.Empty, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
|
|
||||||
// return out if the shim is still servicing containers
|
|
||||||
if len(s.containers) > 0 {
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// please make sure that temporary resource has been cleanup or registered
|
|
||||||
// for cleanup before calling shutdown
|
|
||||||
s.shutdown.Shutdown()
|
|
||||||
|
|
||||||
return empty, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
|
||||||
container, err := s.getContainer(r.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
cgx := container.Cgroup()
|
|
||||||
if cgx == nil {
|
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
|
||||||
}
|
|
||||||
var statsx interface{}
|
|
||||||
switch cg := cgx.(type) {
|
|
||||||
case cgroups.Cgroup:
|
|
||||||
stats, err := cg.Stat(cgroups.IgnoreNotExist)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
statsx = stats
|
|
||||||
case *cgroupsv2.Manager:
|
|
||||||
stats, err := cg.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
statsx = stats
|
|
||||||
default:
|
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrNotImplemented, "unsupported cgroup type %T", cg)
|
|
||||||
}
|
|
||||||
data, err := typeurl.MarshalAny(statsx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &taskAPI.StatsResponse{
|
|
||||||
Stats: data,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) processExits() {
|
|
||||||
for e := range s.ec {
|
|
||||||
s.checkProcesses(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) send(evt interface{}) {
|
|
||||||
s.events <- evt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) sendL(evt interface{}) {
|
|
||||||
s.eventSendMu.Lock()
|
|
||||||
s.events <- evt
|
|
||||||
s.eventSendMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) checkProcesses(e runcC.Exit) {
|
|
||||||
s.mu.Lock()
|
|
||||||
defer s.mu.Unlock()
|
|
||||||
|
|
||||||
for _, container := range s.containers {
|
|
||||||
if !container.HasPid(e.Pid) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range container.All() {
|
|
||||||
if p.Pid() != e.Pid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip, ok := p.(*process.Init); ok {
|
|
||||||
// Ensure all children are killed
|
|
||||||
if runc.ShouldKillAllOnExit(s.context, container.Bundle) {
|
|
||||||
if err := ip.KillAll(s.context); err != nil {
|
|
||||||
logrus.WithError(err).WithField("id", ip.ID()).
|
|
||||||
Error("failed to kill init's children")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.SetExited(e.Status)
|
|
||||||
s.sendL(&eventstypes.TaskExit{
|
|
||||||
ContainerID: container.ID,
|
|
||||||
ID: p.ID(),
|
|
||||||
Pid: uint32(e.Pid),
|
|
||||||
ExitStatus: uint32(e.Status),
|
|
||||||
ExitedAt: p.ExitedAt(),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
|
|
||||||
container, err := s.getContainer(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p, err := container.Process("")
|
|
||||||
if err != nil {
|
|
||||||
return nil, errdefs.ToGRPC(err)
|
|
||||||
}
|
|
||||||
ps, err := p.(*process.Init).Runtime().Ps(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pids := make([]uint32, 0, len(ps))
|
|
||||||
for _, pid := range ps {
|
|
||||||
pids = append(pids, uint32(pid))
|
|
||||||
}
|
|
||||||
return pids, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) forward(ctx context.Context, publisher shim.Publisher) {
|
|
||||||
ns, _ := namespaces.Namespace(ctx)
|
|
||||||
ctx = namespaces.WithNamespace(context.Background(), ns)
|
|
||||||
for e := range s.events {
|
|
||||||
err := publisher.Publish(ctx, runc.GetTopic(e), e)
|
|
||||||
if err != nil {
|
|
||||||
logrus.WithError(err).Error("post event")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
publisher.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *service) getContainer(id string) (*runc.Container, error) {
|
|
||||||
s.mu.Lock()
|
|
||||||
container := s.containers[id]
|
|
||||||
s.mu.Unlock()
|
|
||||||
if container == nil {
|
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "container not created")
|
|
||||||
}
|
|
||||||
return container, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize a single epoll fd to manage our consoles. `initPlatform` should
|
|
||||||
// only be called once.
|
|
||||||
func (s *service) initPlatform() error {
|
|
||||||
if s.platform != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
p, err := runc.NewPlatform()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.platform = p
|
|
||||||
s.shutdown.RegisterCallback(func(context.Context) error { return s.platform.Close() })
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -48,21 +48,37 @@ type Publisher interface {
|
|||||||
|
|
||||||
// StartOpts describes shim start configuration received from containerd
|
// StartOpts describes shim start configuration received from containerd
|
||||||
type StartOpts struct {
|
type StartOpts struct {
|
||||||
ID string
|
ID string // TODO(2.0): Remove ID, passed directly to start for call symmetry
|
||||||
ContainerdBinary string
|
ContainerdBinary string
|
||||||
Address string
|
Address string
|
||||||
TTRPCAddress string
|
TTRPCAddress string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type StopStatus struct {
|
||||||
|
Pid int
|
||||||
|
ExitStatus int
|
||||||
|
ExitedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
// Init func for the creation of a shim server
|
// Init func for the creation of a shim server
|
||||||
|
// TODO(2.0): Remove init function
|
||||||
type Init func(context.Context, string, Publisher, func()) (Shim, error)
|
type Init func(context.Context, string, Publisher, func()) (Shim, error)
|
||||||
|
|
||||||
// Shim server interface
|
// Shim server interface
|
||||||
|
// TODO(2.0): Remove unified shim interface
|
||||||
type Shim interface {
|
type Shim interface {
|
||||||
Cleanup(ctx context.Context) (*shimapi.DeleteResponse, error) // TODO(2.0): Update interface to pass ID directly to Cleanup
|
shimapi.TaskService
|
||||||
|
Cleanup(ctx context.Context) (*shimapi.DeleteResponse, error)
|
||||||
StartShim(ctx context.Context, opts StartOpts) (string, error)
|
StartShim(ctx context.Context, opts StartOpts) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manager is the interface which manages the shim process
|
||||||
|
type Manager interface {
|
||||||
|
Name() string
|
||||||
|
Start(ctx context.Context, id string, opts StartOpts) (string, error)
|
||||||
|
Stop(ctx context.Context, id string) (StopStatus, error)
|
||||||
|
}
|
||||||
|
|
||||||
// OptsKey is the context key for the Opts value.
|
// OptsKey is the context key for the Opts value.
|
||||||
type OptsKey struct{}
|
type OptsKey struct{}
|
||||||
|
|
||||||
@ -90,18 +106,18 @@ type ttrpcService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type taskService struct {
|
type taskService struct {
|
||||||
local shimapi.TaskService
|
shimapi.TaskService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *taskService) RegisterTTRPC(server *ttrpc.Server) error {
|
func (t taskService) RegisterTTRPC(server *ttrpc.Server) error {
|
||||||
shimapi.RegisterTaskService(server, t.local)
|
shimapi.RegisterTaskService(server, t.TaskService)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
debugFlag bool
|
debugFlag bool
|
||||||
versionFlag bool
|
versionFlag bool
|
||||||
idFlag string
|
id string
|
||||||
namespaceFlag string
|
namespaceFlag string
|
||||||
socketFlag string
|
socketFlag string
|
||||||
bundlePath string
|
bundlePath string
|
||||||
@ -118,7 +134,7 @@ func parseFlags() {
|
|||||||
flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs")
|
flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs")
|
||||||
flag.BoolVar(&versionFlag, "v", false, "show the shim version and exit")
|
flag.BoolVar(&versionFlag, "v", false, "show the shim version and exit")
|
||||||
flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim")
|
flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim")
|
||||||
flag.StringVar(&idFlag, "id", "", "id of the task")
|
flag.StringVar(&id, "id", "", "id of the task")
|
||||||
flag.StringVar(&socketFlag, "socket", "", "socket path to serve")
|
flag.StringVar(&socketFlag, "socket", "", "socket path to serve")
|
||||||
flag.StringVar(&bundlePath, "bundle", "", "path to the bundle if not workdir")
|
flag.StringVar(&bundlePath, "bundle", "", "path to the bundle if not workdir")
|
||||||
|
|
||||||
@ -143,37 +159,85 @@ func setRuntime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setLogger(ctx context.Context, id string) error {
|
func setLogger(ctx context.Context, id string) (context.Context, error) {
|
||||||
logrus.SetFormatter(&logrus.TextFormatter{
|
l := log.G(ctx)
|
||||||
|
l.Logger.SetFormatter(&logrus.TextFormatter{
|
||||||
TimestampFormat: log.RFC3339NanoFixed,
|
TimestampFormat: log.RFC3339NanoFixed,
|
||||||
FullTimestamp: true,
|
FullTimestamp: true,
|
||||||
})
|
})
|
||||||
if debugFlag {
|
if debugFlag {
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
l.Logger.SetLevel(logrus.DebugLevel)
|
||||||
}
|
}
|
||||||
f, err := openLog(ctx, id)
|
f, err := openLog(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return ctx, err
|
||||||
}
|
}
|
||||||
logrus.SetOutput(f)
|
l.Logger.SetOutput(f)
|
||||||
return nil
|
return log.WithLogger(ctx, l), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run initializes and runs a shim server
|
// Run initializes and runs a shim server
|
||||||
// TODO(2.0): Remove initFunc from arguments
|
// TODO(2.0): Remove function
|
||||||
func Run(id string, initFunc Init, opts ...BinaryOpts) {
|
func Run(name string, initFunc Init, opts ...BinaryOpts) {
|
||||||
var config Config
|
var config Config
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&config)
|
o(&config)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := run(id, initFunc, config); err != nil {
|
ctx := context.Background()
|
||||||
fmt.Fprintf(os.Stderr, "%s: %s\n", id, err)
|
ctx = log.WithLogger(ctx, log.G(ctx).WithField("runtime", name))
|
||||||
|
|
||||||
|
if err := run(ctx, nil, initFunc, name, config); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: %s", name, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(id string, initFunc Init, config Config) error {
|
// TODO(2.0): Remove this type
|
||||||
|
type shimToManager struct {
|
||||||
|
shim Shim
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stm shimToManager) Name() string {
|
||||||
|
return stm.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stm shimToManager) Start(ctx context.Context, id string, opts StartOpts) (string, error) {
|
||||||
|
opts.ID = id
|
||||||
|
return stm.shim.StartShim(ctx, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stm shimToManager) Stop(ctx context.Context, id string) (StopStatus, error) {
|
||||||
|
// shim must already have id
|
||||||
|
dr, err := stm.shim.Cleanup(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return StopStatus{}, err
|
||||||
|
}
|
||||||
|
return StopStatus{
|
||||||
|
Pid: int(dr.Pid),
|
||||||
|
ExitStatus: int(dr.ExitStatus),
|
||||||
|
ExitedAt: dr.ExitedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunManager initialzes and runs a shim server
|
||||||
|
// TODO(2.0): Rename to Run
|
||||||
|
func RunManager(ctx context.Context, manager Manager, opts ...BinaryOpts) {
|
||||||
|
var config Config
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&config)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = log.WithLogger(ctx, log.G(ctx).WithField("runtime", manager.Name()))
|
||||||
|
|
||||||
|
if err := run(ctx, manager, nil, "", config); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%s: %s", manager.Name(), err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run(ctx context.Context, manager Manager, initFunc Init, name string, config Config) error {
|
||||||
parseFlags()
|
parseFlags()
|
||||||
if versionFlag {
|
if versionFlag {
|
||||||
fmt.Printf("%s:\n", os.Args[0])
|
fmt.Printf("%s:\n", os.Args[0])
|
||||||
@ -208,29 +272,49 @@ func run(id string, initFunc Init, config Config) error {
|
|||||||
}
|
}
|
||||||
defer publisher.Close()
|
defer publisher.Close()
|
||||||
|
|
||||||
ctx := namespaces.WithNamespace(context.Background(), namespaceFlag)
|
ctx = namespaces.WithNamespace(ctx, namespaceFlag)
|
||||||
ctx = context.WithValue(ctx, OptsKey{}, Opts{BundlePath: bundlePath, Debug: debugFlag})
|
ctx = context.WithValue(ctx, OptsKey{}, Opts{BundlePath: bundlePath, Debug: debugFlag})
|
||||||
ctx = log.WithLogger(ctx, log.G(ctx).WithField("runtime", id))
|
|
||||||
ctx, sd := shutdown.WithShutdown(ctx)
|
ctx, sd := shutdown.WithShutdown(ctx)
|
||||||
defer sd.Shutdown()
|
defer sd.Shutdown()
|
||||||
service, err := initFunc(ctx, idFlag, publisher, sd.Shutdown)
|
|
||||||
if err != nil {
|
if manager == nil {
|
||||||
return err
|
service, err := initFunc(ctx, id, publisher, sd.Shutdown)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.TTRPCPlugin,
|
||||||
|
ID: "task",
|
||||||
|
Requires: []plugin.Type{
|
||||||
|
plugin.EventPlugin,
|
||||||
|
},
|
||||||
|
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
return taskService{service}, nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
manager = shimToManager{
|
||||||
|
shim: service,
|
||||||
|
name: name,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle explicit actions
|
// Handle explicit actions
|
||||||
switch action {
|
switch action {
|
||||||
case "delete":
|
case "delete":
|
||||||
logger := logrus.WithFields(logrus.Fields{
|
logger := log.G(ctx).WithFields(logrus.Fields{
|
||||||
"pid": os.Getpid(),
|
"pid": os.Getpid(),
|
||||||
"namespace": namespaceFlag,
|
"namespace": namespaceFlag,
|
||||||
})
|
})
|
||||||
go handleSignals(ctx, logger, signals)
|
go handleSignals(ctx, logger, signals)
|
||||||
response, err := service.Cleanup(ctx)
|
ss, err := manager.Stop(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, err := proto.Marshal(response)
|
data, err := proto.Marshal(&shimapi.DeleteResponse{
|
||||||
|
Pid: uint32(ss.Pid),
|
||||||
|
ExitStatus: uint32(ss.ExitStatus),
|
||||||
|
ExitedAt: ss.ExitedAt,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -240,13 +324,12 @@ func run(id string, initFunc Init, config Config) error {
|
|||||||
return nil
|
return nil
|
||||||
case "start":
|
case "start":
|
||||||
opts := StartOpts{
|
opts := StartOpts{
|
||||||
ID: idFlag,
|
|
||||||
ContainerdBinary: containerdBinaryFlag,
|
ContainerdBinary: containerdBinaryFlag,
|
||||||
Address: addressFlag,
|
Address: addressFlag,
|
||||||
TTRPCAddress: ttrpcAddress,
|
TTRPCAddress: ttrpcAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
address, err := service.StartShim(ctx, opts)
|
address, err := manager.Start(ctx, id, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -257,7 +340,8 @@ func run(id string, initFunc Init, config Config) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !config.NoSetupLogger {
|
if !config.NoSetupLogger {
|
||||||
if err := setLogger(ctx, idFlag); err != nil {
|
ctx, err = setLogger(ctx, id)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,20 +363,6 @@ func run(id string, initFunc Init, config Config) error {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// If service is an implementation of the task service, register it as a plugin
|
|
||||||
if ts, ok := service.(shimapi.TaskService); ok {
|
|
||||||
plugin.Register(&plugin.Registration{
|
|
||||||
Type: plugin.TTRPCPlugin,
|
|
||||||
ID: "task",
|
|
||||||
Requires: []plugin.Type{
|
|
||||||
plugin.EventPlugin,
|
|
||||||
},
|
|
||||||
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
|
|
||||||
return &taskService{ts}, nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
initialized = plugin.NewPluginSet()
|
initialized = plugin.NewPluginSet()
|
||||||
ttrpcServices = []ttrpcService{}
|
ttrpcServices = []ttrpcService{}
|
||||||
@ -397,10 +467,10 @@ func serve(ctx context.Context, server *ttrpc.Server, signals chan os.Signal) er
|
|||||||
defer l.Close()
|
defer l.Close()
|
||||||
if err := server.Serve(ctx, l); err != nil &&
|
if err := server.Serve(ctx, l); err != nil &&
|
||||||
!strings.Contains(err.Error(), "use of closed network connection") {
|
!strings.Contains(err.Error(), "use of closed network connection") {
|
||||||
logrus.WithError(err).Fatal("containerd-shim: ttrpc server failure")
|
log.G(ctx).WithError(err).Fatal("containerd-shim: ttrpc server failure")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
logger := logrus.WithFields(logrus.Fields{
|
logger := log.G(ctx).WithFields(logrus.Fields{
|
||||||
"pid": os.Getpid(),
|
"pid": os.Getpid(),
|
||||||
"path": path,
|
"path": path,
|
||||||
"namespace": namespaceFlag,
|
"namespace": namespaceFlag,
|
||||||
|
Loading…
Reference in New Issue
Block a user