Merge pull request #9442 from AkihiroSuda/runtime-info2

api/services/instrospection: add PluginInfo
This commit is contained in:
Maksym Pavlenko
2024-01-25 17:50:42 +00:00
committed by GitHub
24 changed files with 1423 additions and 204 deletions

View File

@@ -21,6 +21,7 @@ import (
"github.com/containerd/typeurl/v2"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-spec/specs-go/features"
)
func init() {
@@ -31,4 +32,5 @@ func init() {
typeurl.Register(&specs.Process{}, prefix, "opencontainers/runtime-spec", major, "Process")
typeurl.Register(&specs.LinuxResources{}, prefix, "opencontainers/runtime-spec", major, "LinuxResources")
typeurl.Register(&specs.WindowsResources{}, prefix, "opencontainers/runtime-spec", major, "WindowsResources")
typeurl.Register(&features.Features{}, prefix, "opencontainers/runtime-spec", major, "features", "Features")
}

View File

@@ -243,6 +243,29 @@ The delete command MUST accept the following flags:
The delete command will be executed in the container's bundle as its `cwd` except for on Windows and FreeBSD platforms.
### Command-like flags
#### `-v`
Each shim SHOULD implement a `-v` flag.
This command-like flag prints the shim implementation version and exits.
The output is not machine-parsable.
#### `-info`
Each shim SHOULD implement a `-info` flag.
This command-like flag gets the option protobuf from stdin, prints the shim info protobuf (see below) to stdout, and exits.
```proto
message RuntimeInfo {
string name = 1;
RuntimeVersion version = 2;
// Options from stdin
google.protobuf.Any options = 3;
// OCI-compatible runtimes should use https://github.com/opencontainers/runtime-spec/blob/main/features.md
google.protobuf.Any features = 4;
// Annotations of the shim. Irrelevant to features.Annotations.
map<string, string> annotations = 5;
}
```
### Host Level Shim Configuration
containerd does not provide any host level configuration for shims via the API.

View File

@@ -18,9 +18,11 @@ package example
import (
"context"
"io"
"os"
taskAPI "github.com/containerd/containerd/v2/api/runtime/task/v2"
apitypes "github.com/containerd/containerd/v2/api/types"
"github.com/containerd/containerd/v2/core/runtime/v2/shim"
"github.com/containerd/containerd/v2/pkg/errdefs"
"github.com/containerd/containerd/v2/pkg/shutdown"
@@ -73,6 +75,16 @@ func (m manager) Stop(ctx context.Context, id string) (shim.StopStatus, error) {
return shim.StopStatus{}, errdefs.ErrNotImplemented
}
func (m manager) Info(ctx context.Context, optionsR io.Reader) (*apitypes.RuntimeInfo, error) {
info := &apitypes.RuntimeInfo{
Name: "io.containerd.example.v1",
Version: &apitypes.RuntimeVersion{
Version: "v1.0.0",
},
}
return info, nil
}
func newTaskService(ctx context.Context, publisher shim.Publisher, sd shutdown.Service) (taskAPI.TaskService, error) {
// The shim.Publisher and shutdown.Service are usually useful for your task service,
// but we don't need them in the exampleTaskService.

View File

@@ -17,6 +17,7 @@
package v2
import (
"bytes"
"context"
"errors"
"fmt"
@@ -26,6 +27,7 @@ import (
"strings"
"sync"
apitypes "github.com/containerd/containerd/v2/api/types"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/core/metadata"
"github.com/containerd/containerd/v2/core/runtime"
@@ -38,6 +40,7 @@ import (
"github.com/containerd/containerd/v2/pkg/timeout"
"github.com/containerd/containerd/v2/plugins"
"github.com/containerd/containerd/v2/protobuf"
"github.com/containerd/containerd/v2/protobuf/proto"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/containerd/plugin"
@@ -533,3 +536,35 @@ func (m *TaskManager) Delete(ctx context.Context, taskID string) (*runtime.Exit,
return exit, nil
}
func (m *TaskManager) PluginInfo(ctx context.Context, request interface{}) (interface{}, error) {
req, ok := request.(*apitypes.RuntimeRequest)
if !ok {
return nil, fmt.Errorf("unknown request type %T: %w", request, errdefs.ErrNotImplemented)
}
runtimePath, err := m.manager.resolveRuntimePath(req.RuntimePath)
if err != nil {
return nil, fmt.Errorf("failed to resolve runtime path: %w", err)
}
var optsB []byte
if req.Options != nil {
optsB, err = proto.Marshal(req.Options)
if err != nil {
return nil, fmt.Errorf("failed to marshal %s: %w", req.Options.TypeUrl, err)
}
}
var stderr bytes.Buffer
cmd := exec.CommandContext(ctx, runtimePath, "-info")
cmd.Stdin = bytes.NewReader(optsB)
cmd.Stderr = &stderr
stdout, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to run %v: %w (stderr: %q)", cmd.Args, err, stderr.String())
}
var info apitypes.RuntimeInfo
if err = proto.Unmarshal(stdout, &info); err != nil {
return nil, fmt.Errorf("failed to unmarshal stdout from %v into %T: %w", cmd.Args, &info, err)
}
return &info, nil
}

View File

@@ -31,6 +31,7 @@ import (
"time"
shimapi "github.com/containerd/containerd/v2/api/runtime/task/v3"
"github.com/containerd/containerd/v2/api/types"
"github.com/containerd/containerd/v2/pkg/events"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/pkg/shutdown"
@@ -79,6 +80,7 @@ type Manager interface {
Name() string
Start(ctx context.Context, id string, opts StartOpts) (BootstrapParams, error)
Stop(ctx context.Context, id string) (StopStatus, error)
Info(ctx context.Context, optionsR io.Reader) (*types.RuntimeInfo, error)
}
// OptsKey is the context key for the Opts value.
@@ -116,6 +118,7 @@ type TTRPCServerOptioner interface {
var (
debugFlag bool
versionFlag bool
infoFlag bool
id string
namespaceFlag string
socketFlag string
@@ -135,6 +138,9 @@ const (
func parseFlags() {
flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs")
flag.BoolVar(&versionFlag, "v", false, "show the shim version and exit")
// "info" is not a subcommand, because old shims produce very confusing errors for unknown subcommands
// https://github.com/containerd/containerd/pull/8509#discussion_r1210021403
flag.BoolVar(&infoFlag, "info", false, "get the option protobuf from stdin, print the shim info protobuf to stdout, and exit")
flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim")
flag.StringVar(&id, "id", "", "id of the task")
flag.StringVar(&socketFlag, "socket", "", "socket path to serve")
@@ -195,6 +201,19 @@ func Run(ctx context.Context, manager Manager, opts ...BinaryOpts) {
}
}
func runInfo(ctx context.Context, manager Manager) error {
info, err := manager.Info(ctx, os.Stdin)
if err != nil {
return err
}
infoB, err := proto.Marshal(info)
if err != nil {
return err
}
_, err = os.Stdout.Write(infoB)
return err
}
func run(ctx context.Context, manager Manager, config Config) error {
parseFlags()
if versionFlag {
@@ -206,6 +225,10 @@ func run(ctx context.Context, manager Manager, config Config) error {
return nil
}
if infoFlag {
return runInfo(ctx, manager)
}
if namespaceFlag == "" {
return fmt.Errorf("shim namespace cannot be empty")
}