mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
472 lines
16 KiB
Go
472 lines
16 KiB
Go
// Copyright 2018 The gVisor 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
|
|
//
|
|
// https://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 v1 implements Containerd Shim v2 interface.
|
|
package v1
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"os"
|
|
|
|
task "github.com/containerd/containerd/api/runtime/task/v2"
|
|
types "github.com/containerd/containerd/v2/pkg/protobuf/types"
|
|
"github.com/containerd/containerd/v2/pkg/shim"
|
|
"github.com/containerd/containerd/v2/pkg/shutdown"
|
|
errgrpc "github.com/containerd/errdefs/pkg/errgrpc"
|
|
"github.com/containerd/log"
|
|
"github.com/containerd/ttrpc"
|
|
taskServer "gvisor.dev/gvisor/pkg/shim/v1/taskserver"
|
|
|
|
api "github.com/containerd/containerd/api/runtime/sandbox/v1"
|
|
"github.com/containerd/errdefs"
|
|
|
|
"gvisor.dev/gvisor/pkg/shim/v1/extension"
|
|
rsc "gvisor.dev/gvisor/pkg/shim/v1/runsc"
|
|
"gvisor.dev/gvisor/pkg/sync"
|
|
)
|
|
|
|
const (
|
|
// shimAddressPath is the relative path to a file that contains the address
|
|
// to the shim UDS. See service.shimAddress.
|
|
shimAddressPath = "address"
|
|
)
|
|
|
|
// NewShimRedirector creates a new shim service that integrates with runsc.
|
|
func NewShimRedirector(ctx context.Context, publisher shim.Publisher, sd shutdown.Service) (extension.TaskServiceExt, error) {
|
|
runsc, err := rsc.NewTaskService(ctx, publisher, sd)
|
|
if err != nil {
|
|
sd.Shutdown()
|
|
return nil, err
|
|
}
|
|
|
|
runtimeOptions := rsc.GetRuntimeOptions()
|
|
|
|
s := &shimRedirector{
|
|
shutdown: sd,
|
|
main: runsc,
|
|
runtimeOptions: runtimeOptions,
|
|
}
|
|
|
|
if runtimeOptions.EnableHibernateServer && isDaemon() {
|
|
idFlag := flag.Lookup("id")
|
|
if idFlag != nil && idFlag.Value.String() != "" {
|
|
id := idFlag.Value.String()
|
|
rootDir := runtimeOptions.Root
|
|
if rootDir == "" {
|
|
rootDir = "/run/containerd/runsc"
|
|
}
|
|
ts, err := taskServer.NewServer(rootDir, id)
|
|
if err != nil {
|
|
log.L.Errorf("Failed to create task server: %v", err)
|
|
} else {
|
|
adapter := rsc.NewGvisorTaskServer(runsc)
|
|
if adapter == nil {
|
|
log.L.Errorf("Failed to create task server adapter: service is not *runscService")
|
|
} else {
|
|
ts.RegisterService(adapter)
|
|
go func() {
|
|
log.L.Infof("Starting task server at %s", ts.Address())
|
|
if err := ts.Serve(ctx); err != nil {
|
|
log.L.Errorf("Task server failed: %v", err)
|
|
}
|
|
}()
|
|
sd.RegisterCallback(func(ctx context.Context) error {
|
|
log.L.Infof("Shutting down task server")
|
|
return ts.Shutdown(ctx)
|
|
})
|
|
}
|
|
}
|
|
} else {
|
|
log.L.Warnf("Task server enabled but ID flag not found or empty")
|
|
}
|
|
}
|
|
|
|
if address, _ := shim.ReadAddress(shimAddressPath); len(address) > 0 {
|
|
sd.RegisterCallback(func(context.Context) error {
|
|
shim.RemoveSocket(address)
|
|
return nil
|
|
})
|
|
}
|
|
|
|
s.grouping = runtimeOptions.Grouping
|
|
return s, nil
|
|
}
|
|
|
|
// shimRedirector is the implementation of task service extension over gRPC/ttrpc. It
|
|
// is a intermediate layer between the containerd API and the runsc task service.
|
|
//
|
|
// It intercepts some calls to the task service and forwards all calls
|
|
// to extension.TaskServiceExt which actually implements the service interface.
|
|
type shimRedirector struct {
|
|
mu sync.Mutex
|
|
|
|
// shimAddress is the location of the UDS used to communicate to containerd.
|
|
shimAddress string
|
|
|
|
// main is the extension.TaskServiceExt that is used for all calls to the
|
|
// container's shim, except for the cases where `ext` is set.
|
|
//
|
|
// Protected by mu.
|
|
main extension.TaskServiceExt
|
|
|
|
// ext may intercept calls to the container's shim. During the call to create
|
|
// container, the extension may be created and the shim will start using it
|
|
// for all calls to the container's shim.
|
|
//
|
|
// Protected by mu.
|
|
ext extension.TaskServiceExt
|
|
|
|
// grouping indicates if shim grouping is enabled.
|
|
grouping bool
|
|
|
|
shutdown shutdown.Service
|
|
runtimeOptions *rsc.Options
|
|
}
|
|
|
|
var _ extension.TaskServiceExt = (*shimRedirector)(nil)
|
|
|
|
// Preconditions:
|
|
// - s.mu must be locked
|
|
func (s *shimRedirector) getLocked() extension.TaskServiceExt {
|
|
if s.ext == nil {
|
|
return s.main
|
|
}
|
|
return s.ext
|
|
}
|
|
|
|
// get return the extension.TaskServiceExt that should be used for the next
|
|
// call to the container's shim.
|
|
func (s *shimRedirector) get() extension.TaskServiceExt {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
return s.getLocked()
|
|
}
|
|
|
|
const kubernetesGroupAnnotation = "io.kubernetes.cri.sandbox-id"
|
|
|
|
type spec struct {
|
|
// Annotations contains arbitrary metadata for the container.
|
|
Annotations map[string]string `json:"annotations,omitempty"`
|
|
}
|
|
|
|
// isDaemon ensures we only create sockets and run Serve() in the main background process,
|
|
// ignoring short-lived containerd helpers like delete or state.
|
|
func isDaemon() bool {
|
|
for _, arg := range os.Args {
|
|
if arg == "delete" || arg == "state" || arg == "info" || arg == "stats" {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// initExt initializes the extension that may intercept calls to the container's shim.
|
|
func (s *shimRedirector) initExt(ctx context.Context, r *task.CreateTaskRequest) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if s.grouping {
|
|
// Create shim extension if required.
|
|
if s.ext == nil && extension.NewPodExtension != nil {
|
|
log.L.Debugf("Create shim extension per pod")
|
|
var err error
|
|
s.ext, err = extension.NewPodExtension(ctx, s.main, r)
|
|
if err != nil {
|
|
log.L.Debugf("Creating shim extension per pod failed with error: %v", err)
|
|
return err
|
|
}
|
|
}
|
|
} else {
|
|
if extension.NewExtension != nil {
|
|
log.L.Debugf("Create shim extension per container")
|
|
var err error
|
|
s.ext, err = extension.NewExtension(ctx, s.main, r)
|
|
if err != nil {
|
|
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Create creates a new initial process and container with the underlying OCI
|
|
// runtime.
|
|
func (s *shimRedirector) Create(ctx context.Context, r *task.CreateTaskRequest) (*task.CreateTaskResponse, error) {
|
|
log.L.Debugf("Create, id: %s, bundle: %q", r.ID, r.Bundle)
|
|
if err := s.initExt(ctx, r); err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := s.get().Create(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// CreateWithFSRestore creates a container which restores its filesystem from a snapshot.
|
|
func (s *shimRedirector) CreateWithFSRestore(ctx context.Context, r *extension.CreateWithFSRestoreRequest) (*task.CreateTaskResponse, error) {
|
|
log.L.Debugf("CreateWithFSRestore, id: %s, bundle: %q", r.Create.ID, r.Create.Bundle)
|
|
if err := s.initExt(ctx, r.Create); err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := s.get().CreateWithFSRestore(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Start starts the container.
|
|
func (s *shimRedirector) Start(ctx context.Context, r *task.StartRequest) (*task.StartResponse, error) {
|
|
log.L.Debugf("Start, id: %s, execID: %s", r.ID, r.ExecID)
|
|
resp, err := s.get().Start(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Delete deletes container.
|
|
func (s *shimRedirector) Delete(ctx context.Context, r *task.DeleteRequest) (*task.DeleteResponse, error) {
|
|
log.L.Debugf("Delete, id: %s, execID: %s", r.ID, r.ExecID)
|
|
resp, err := s.get().Delete(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Exec spawns a process inside the container.
|
|
func (s *shimRedirector) Exec(ctx context.Context, r *task.ExecProcessRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Exec, id: %s, execID: %s", r.ID, r.ExecID)
|
|
resp, err := s.get().Exec(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// ResizePty resizes the terminal of a process.
|
|
func (s *shimRedirector) ResizePty(ctx context.Context, r *task.ResizePtyRequest) (*types.Empty, error) {
|
|
log.L.Debugf("ResizePty, id: %s, execID: %s, dimension: %dx%d", r.ID, r.ExecID, r.Height, r.Width)
|
|
resp, err := s.get().ResizePty(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// State returns runtime state information for the container.
|
|
func (s *shimRedirector) State(ctx context.Context, r *task.StateRequest) (*task.StateResponse, error) {
|
|
log.L.Debugf("State, id: %s, execID: %s", r.ID, r.ExecID)
|
|
resp, err := s.get().State(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Pause the container.
|
|
func (s *shimRedirector) Pause(ctx context.Context, r *task.PauseRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Pause, id: %s", r.ID)
|
|
resp, err := s.get().Pause(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Resume the container.
|
|
func (s *shimRedirector) Resume(ctx context.Context, r *task.ResumeRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Resume, id: %s", r.ID)
|
|
resp, err := s.get().Resume(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Kill the container with the provided signal.
|
|
func (s *shimRedirector) Kill(ctx context.Context, r *task.KillRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Kill, id: %s, execID: %s, signal: %d, all: %t", r.ID, r.ExecID, r.Signal, r.All)
|
|
resp, err := s.get().Kill(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Pids returns all pids inside the container.
|
|
func (s *shimRedirector) Pids(ctx context.Context, r *task.PidsRequest) (*task.PidsResponse, error) {
|
|
log.L.Debugf("Pids, id: %s", r.ID)
|
|
resp, err := s.get().Pids(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// CloseIO closes the I/O context of the container.
|
|
func (s *shimRedirector) CloseIO(ctx context.Context, r *task.CloseIORequest) (*types.Empty, error) {
|
|
log.L.Debugf("CloseIO, id: %s, execID: %s, stdin: %t", r.ID, r.ExecID, r.Stdin)
|
|
resp, err := s.get().CloseIO(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Checkpoint checkpoints the container.
|
|
func (s *shimRedirector) Checkpoint(ctx context.Context, r *task.CheckpointTaskRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Checkpoint, id: %s", r.ID)
|
|
resp, err := s.get().Checkpoint(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Connect returns shim information such as the shim's pid.
|
|
func (s *shimRedirector) Connect(ctx context.Context, r *task.ConnectRequest) (*task.ConnectResponse, error) {
|
|
log.L.Debugf("Connect, id: %s", r.ID)
|
|
resp, err := s.get().Connect(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
func (s *shimRedirector) Shutdown(ctx context.Context, r *task.ShutdownRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Shutdown, id: %s", r.ID)
|
|
resp, err := s.get().Shutdown(ctx, r)
|
|
if err != nil {
|
|
// The Shutdown call should return a nil error even if the shim did not
|
|
// shutdown due to running containers. Returning nil indicates that the
|
|
// request was successfully processed by the shim, regardless of whether the
|
|
// shim was exited. This aligns with standard runc behavior, where the shim
|
|
// stays alive as long as containers are present but acknowledges the shutdown
|
|
// signal without error. The error from runscService's Shutdown call is used
|
|
// as an indicator to identify if there are any active containers in the shim.
|
|
log.L.Debugf("Shutdown, shim did not shutdown due to: %v", err)
|
|
return resp, errgrpc.ToGRPC(nil)
|
|
}
|
|
s.shutdown.Shutdown()
|
|
return resp, nil
|
|
}
|
|
|
|
func (s *shimRedirector) Stats(ctx context.Context, r *task.StatsRequest) (*task.StatsResponse, error) {
|
|
log.L.Debugf("Stats, id: %s", r.ID)
|
|
resp, err := s.get().Stats(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Update updates a running container.
|
|
func (s *shimRedirector) Update(ctx context.Context, r *task.UpdateTaskRequest) (*types.Empty, error) {
|
|
log.L.Debugf("Update, id: %s", r.ID)
|
|
resp, err := s.get().Update(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Wait waits for the container to exit.
|
|
func (s *shimRedirector) Wait(ctx context.Context, r *task.WaitRequest) (*task.WaitResponse, error) {
|
|
log.L.Debugf("Wait, id: %s, execID: %s", r.ID, r.ExecID)
|
|
resp, err := s.get().Wait(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Restore restores the container.
|
|
func (s *shimRedirector) Restore(ctx context.Context, r *extension.RestoreRequest) (*task.StartResponse, error) {
|
|
log.L.Debugf("Restore, id: %s", r.Start.ID)
|
|
resp, err := s.get().Restore(ctx, r)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
func (s *shimRedirector) RegisterTTRPC(server *ttrpc.Server) error {
|
|
task.RegisterTaskService(server, s)
|
|
api.RegisterTTRPCSandboxService(server, s)
|
|
return nil
|
|
}
|
|
|
|
func (s *shimRedirector) getSandboxService() (api.TTRPCSandboxService, error) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if srv, ok := s.getLocked().(api.TTRPCSandboxService); ok {
|
|
return srv, nil
|
|
}
|
|
if srv, ok := s.main.(api.TTRPCSandboxService); ok {
|
|
return srv, nil
|
|
}
|
|
return nil, errdefs.ErrNotImplemented
|
|
}
|
|
|
|
// CreateSandbox implements api.TTRPCSandboxService.CreateSandbox.
|
|
func (s *shimRedirector) CreateSandbox(ctx context.Context, req *api.CreateSandboxRequest) (*api.CreateSandboxResponse, error) {
|
|
log.L.Debugf("CreateSandbox redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.CreateSandbox(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// StartSandbox implements api.TTRPCSandboxService.StartSandbox.
|
|
func (s *shimRedirector) StartSandbox(ctx context.Context, req *api.StartSandboxRequest) (*api.StartSandboxResponse, error) {
|
|
log.L.Debugf("StartSandbox redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.StartSandbox(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// Platform implements api.TTRPCSandboxService.Platform.
|
|
func (s *shimRedirector) Platform(ctx context.Context, req *api.PlatformRequest) (*api.PlatformResponse, error) {
|
|
log.L.Debugf("Platform redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.Platform(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// StopSandbox implements api.TTRPCSandboxService.StopSandbox.
|
|
func (s *shimRedirector) StopSandbox(ctx context.Context, req *api.StopSandboxRequest) (*api.StopSandboxResponse, error) {
|
|
log.L.Debugf("StopSandbox redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.StopSandbox(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// WaitSandbox implements api.TTRPCSandboxService.WaitSandbox.
|
|
func (s *shimRedirector) WaitSandbox(ctx context.Context, req *api.WaitSandboxRequest) (*api.WaitSandboxResponse, error) {
|
|
log.L.Debugf("WaitSandbox redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.WaitSandbox(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// SandboxStatus implements api.TTRPCSandboxService.SandboxStatus.
|
|
func (s *shimRedirector) SandboxStatus(ctx context.Context, req *api.SandboxStatusRequest) (*api.SandboxStatusResponse, error) {
|
|
log.L.Debugf("SandboxStatus redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.SandboxStatus(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// PingSandbox implements api.TTRPCSandboxService.PingSandbox.
|
|
func (s *shimRedirector) PingSandbox(ctx context.Context, req *api.PingRequest) (*api.PingResponse, error) {
|
|
log.L.Debugf("PingSandbox redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.PingSandbox(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// ShutdownSandbox implements api.TTRPCSandboxService.ShutdownSandbox.
|
|
func (s *shimRedirector) ShutdownSandbox(ctx context.Context, req *api.ShutdownSandboxRequest) (*api.ShutdownSandboxResponse, error) {
|
|
log.L.Debugf("ShutdownSandbox redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.ShutdownSandbox(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|
|
|
|
// SandboxMetrics implements api.TTRPCSandboxService.SandboxMetrics.
|
|
func (s *shimRedirector) SandboxMetrics(ctx context.Context, req *api.SandboxMetricsRequest) (*api.SandboxMetricsResponse, error) {
|
|
log.L.Debugf("SandboxMetrics redirector, id: %s", req.SandboxID)
|
|
srv, err := s.getSandboxService()
|
|
if err != nil {
|
|
return nil, errgrpc.ToGRPC(err)
|
|
}
|
|
resp, err := srv.SandboxMetrics(ctx, req)
|
|
return resp, errgrpc.ToGRPC(err)
|
|
}
|