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

@@ -17,9 +17,12 @@
package manager
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
@@ -30,16 +33,21 @@ import (
"github.com/containerd/cgroups/v3"
"github.com/containerd/cgroups/v3/cgroup1"
cgroupsv2 "github.com/containerd/cgroups/v3/cgroup2"
"github.com/containerd/containerd/v2/api/types"
"github.com/containerd/containerd/v2/cmd/containerd-shim-runc-v2/process"
"github.com/containerd/containerd/v2/cmd/containerd-shim-runc-v2/runc"
"github.com/containerd/containerd/v2/core/mount"
"github.com/containerd/containerd/v2/core/runtime/v2/runc/options"
"github.com/containerd/containerd/v2/core/runtime/v2/shim"
"github.com/containerd/containerd/v2/pkg/errdefs"
"github.com/containerd/containerd/v2/pkg/namespaces"
"github.com/containerd/containerd/v2/pkg/oci"
"github.com/containerd/containerd/v2/pkg/schedcore"
"github.com/containerd/containerd/v2/protobuf"
"github.com/containerd/containerd/v2/version"
runcC "github.com/containerd/go-runc"
"github.com/containerd/log"
"github.com/opencontainers/runtime-spec/specs-go/features"
"golang.org/x/sys/unix"
)
@@ -272,3 +280,63 @@ func (manager) Stop(ctx context.Context, id string) (shim.StopStatus, error) {
Pid: pid,
}, nil
}
func (m manager) Info(ctx context.Context, optionsR io.Reader) (*types.RuntimeInfo, error) {
info := &types.RuntimeInfo{
Name: m.name,
Version: &types.RuntimeVersion{
Version: version.Version,
Revision: version.Version,
},
Annotations: nil,
}
binaryName := runcC.DefaultCommand
opts, err := shim.ReadRuntimeOptions[*options.Options](optionsR)
if err != nil {
if !errors.Is(err, errdefs.ErrNotFound) {
return nil, fmt.Errorf("failed to read runtime options (*options.Options): %w", err)
}
}
if opts != nil {
info.Options, err = protobuf.MarshalAnyToProto(opts)
if err != nil {
return nil, fmt.Errorf("failed to marshal %T: %w", opts, err)
}
if opts.BinaryName != "" {
binaryName = opts.BinaryName
}
}
absBinary, err := exec.LookPath(binaryName)
if err != nil {
return nil, fmt.Errorf("failed to look up the path of %q: %w", binaryName, err)
}
features, err := m.features(ctx, absBinary, opts)
if err != nil {
// youki does not implement `runc features` yet, at the time of writing this (Sep 2023)
// https://github.com/containers/youki/issues/815
log.G(ctx).WithError(err).Debug("Failed to get the runtime features. The runc binary does not implement `runc features` command?")
}
if features != nil {
info.Features, err = protobuf.MarshalAnyToProto(features)
if err != nil {
return nil, fmt.Errorf("failed to marshal %T: %w", features, err)
}
}
return info, nil
}
func (m manager) features(ctx context.Context, absBinary string, opts *options.Options) (*features.Features, error) {
var stderr bytes.Buffer
cmd := exec.CommandContext(ctx, absBinary, "features")
cmd.Stderr = &stderr
stdout, err := cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to execute %v: %w (stderr: %q)", cmd.Args, err, stderr.String())
}
var feat features.Features
if err := json.Unmarshal(stdout, &feat); err != nil {
return nil, err
}
return &feat, nil
}

View File

@@ -96,6 +96,19 @@ var (
},
}
// RuntimeFlags are cli flags specifying runtime
RuntimeFlags = []cli.Flag{
cli.StringFlag{
Name: "runtime",
Usage: "Runtime name or absolute path to runtime binary",
Value: defaults.DefaultRuntime,
},
cli.StringFlag{
Name: "runtime-config-path",
Usage: "Optional runtime config path",
},
}
// ContainerFlags are cli flags specifying container options
ContainerFlags = []cli.Flag{
cli.StringFlag{
@@ -138,19 +151,10 @@ var (
Name: "read-only",
Usage: "Set the containers filesystem as readonly",
},
cli.StringFlag{
Name: "runtime",
Usage: "Runtime name or absolute path to runtime binary",
Value: defaults.DefaultRuntime,
},
cli.StringFlag{
Name: "sandbox",
Usage: "Create the container in the given sandbox",
},
cli.StringFlag{
Name: "runtime-config-path",
Usage: "Optional runtime config path",
},
cli.BoolFlag{
Name: "tty,t",
Usage: "Allocate a TTY for the container",

View File

@@ -19,10 +19,24 @@
package commands
import (
"errors"
"github.com/containerd/containerd/v2/core/runtime/v2/runc/options"
runtimeoptions "github.com/containerd/containerd/v2/pkg/runtimeoptions/v1"
"github.com/urfave/cli"
)
func init() {
RuntimeFlags = append(RuntimeFlags, cli.StringFlag{
Name: "runc-binary",
Usage: "Specify runc-compatible binary",
}, cli.StringFlag{
Name: "runc-root",
Usage: "Specify runc-compatible root",
}, cli.BoolFlag{
Name: "runc-systemd-cgroup",
Usage: "Start runc with systemd cgroup manager",
})
ContainerFlags = append(ContainerFlags, cli.BoolFlag{
Name: "rootfs",
Usage: "Use custom rootfs that is not managed by containerd snapshotter",
@@ -44,3 +58,42 @@ func init() {
Usage: "File path to a device to add to the container; or a path to a directory tree of devices to add to the container",
})
}
func getRuncOptions(context *cli.Context) (*options.Options, error) {
runtimeOpts := &options.Options{}
if runcBinary := context.String("runc-binary"); runcBinary != "" {
runtimeOpts.BinaryName = runcBinary
}
if context.Bool("runc-systemd-cgroup") {
if context.String("cgroup") == "" {
// runc maps "machine.slice:foo:deadbeef" to "/machine.slice/foo-deadbeef.scope"
return nil, errors.New("option --runc-systemd-cgroup requires --cgroup to be set, e.g. \"machine.slice:foo:deadbeef\"")
}
runtimeOpts.SystemdCgroup = true
}
if root := context.String("runc-root"); root != "" {
runtimeOpts.Root = root
}
return runtimeOpts, nil
}
func RuntimeOptions(context *cli.Context) (interface{}, error) {
// validate first
if (context.String("runc-binary") != "" || context.Bool("runc-systemd-cgroup")) &&
context.String("runtime") != "io.containerd.runc.v2" {
return nil, errors.New("specifying runc-binary and runc-systemd-cgroup is only supported for \"io.containerd.runc.v2\" runtime")
}
if context.String("runtime") == "io.containerd.runc.v2" {
return getRuncOptions(context)
}
if configPath := context.String("runtime-config-path"); configPath != "" {
return &runtimeoptions.Options{
ConfigPath: configPath,
}, nil
}
return nil, nil
}

View File

@@ -38,3 +38,7 @@ func init() {
Usage: "Identifier of a device to add to the container (e.g. class://5B45201D-F2F2-4F3B-85BB-30FF1F953599)",
})
}
func RuntimeOptions(context *cli.Context) (interface{}, error) {
return nil, nil
}

View File

@@ -55,7 +55,7 @@ var createCommand = cli.Command{
Usage: "Create container",
ArgsUsage: "[flags] Image|RootFS CONTAINER [COMMAND] [ARG...]",
SkipArgReorder: true,
Flags: append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...), commands.ContainerFlags...),
Flags: append(commands.RuntimeFlags, append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...), commands.ContainerFlags...)...),
Action: func(context *cli.Context) error {
var (
id string

View File

@@ -17,6 +17,7 @@
package plugins
import (
"encoding/json"
"fmt"
"os"
"sort"
@@ -39,6 +40,7 @@ var Command = cli.Command{
Usage: "Provides information about containerd plugins",
Subcommands: []cli.Command{
listCommand,
inspectRuntimeCommand,
},
}
@@ -162,3 +164,32 @@ func prettyPlatforms(pspb []*types.Platform) string {
sort.Stable(sort.StringSlice(ps))
return strings.Join(ps, ",")
}
var inspectRuntimeCommand = cli.Command{
Name: "inspect-runtime",
Usage: "Display runtime info",
ArgsUsage: "[flags]",
Flags: commands.RuntimeFlags,
Action: func(context *cli.Context) error {
rt := context.String("runtime")
rtOptions, err := commands.RuntimeOptions(context)
if err != nil {
return err
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
rtInfo, err := client.RuntimeInfo(ctx, rt, rtOptions)
if err != nil {
return err
}
j, err := json.MarshalIndent(rtInfo, "", " ")
if err != nil {
return err
}
_, err = fmt.Fprintln(context.App.Writer, string(j))
return err
},
}

View File

@@ -128,8 +128,9 @@ var Command = cli.Command{
Usage: "Enable cni networking for the container",
},
}, append(platformRunFlags,
append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...),
commands.ContainerFlags...)...)...),
append(commands.RuntimeFlags,
append(append(commands.SnapshotterFlags, []cli.Flag{commands.SnapshotterLabels}...),
commands.ContainerFlags...)...)...)...),
Action: func(context *cli.Context) error {
var (
err error

View File

@@ -34,10 +34,8 @@ import (
"github.com/containerd/containerd/v2/contrib/nvidia"
"github.com/containerd/containerd/v2/contrib/seccomp"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/core/runtime/v2/runc/options"
"github.com/containerd/containerd/v2/core/snapshots"
"github.com/containerd/containerd/v2/pkg/oci"
runtimeoptions "github.com/containerd/containerd/v2/pkg/runtimeoptions/v1"
"github.com/containerd/log"
"github.com/containerd/platforms"
"github.com/intel/goresctrl/pkg/blockio"
@@ -48,18 +46,6 @@ import (
)
var platformRunFlags = []cli.Flag{
cli.StringFlag{
Name: "runc-binary",
Usage: "Specify runc-compatible binary",
},
cli.StringFlag{
Name: "runc-root",
Usage: "Specify runc-compatible root",
},
cli.BoolFlag{
Name: "runc-systemd-cgroup",
Usage: "Start runc with systemd cgroup manager",
},
cli.StringFlag{
Name: "uidmap",
Usage: "Run inside a user namespace with the specified UID mapping range; specified with the format `container-uid:host-uid:length`",
@@ -413,7 +399,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts = append(cOpts, containerd.WithContainerExtension(commands.CtrCniMetadataExtension, cniMeta))
}
runtimeOpts, err := getRuntimeOptions(context)
runtimeOpts, err := commands.RuntimeOptions(context)
if err != nil {
return nil, err
}
@@ -430,45 +416,6 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
return client.NewContainer(ctx, id, cOpts...)
}
func getRuncOptions(context *cli.Context) (*options.Options, error) {
runtimeOpts := &options.Options{}
if runcBinary := context.String("runc-binary"); runcBinary != "" {
runtimeOpts.BinaryName = runcBinary
}
if context.Bool("runc-systemd-cgroup") {
if context.String("cgroup") == "" {
// runc maps "machine.slice:foo:deadbeef" to "/machine.slice/foo-deadbeef.scope"
return nil, errors.New("option --runc-systemd-cgroup requires --cgroup to be set, e.g. \"machine.slice:foo:deadbeef\"")
}
runtimeOpts.SystemdCgroup = true
}
if root := context.String("runc-root"); root != "" {
runtimeOpts.Root = root
}
return runtimeOpts, nil
}
func getRuntimeOptions(context *cli.Context) (interface{}, error) {
// validate first
if (context.String("runc-binary") != "" || context.Bool("runc-systemd-cgroup")) &&
context.String("runtime") != "io.containerd.runc.v2" {
return nil, errors.New("specifying runc-binary and runc-systemd-cgroup is only supported for \"io.containerd.runc.v2\" runtime")
}
if context.String("runtime") == "io.containerd.runc.v2" {
return getRuncOptions(context)
}
if configPath := context.String("runtime-config-path"); configPath != "" {
return &runtimeoptions.Options{
ConfigPath: configPath,
}, nil
}
return nil, nil
}
func parseIDMapping(mapping string) (specs.LinuxIDMapping, error) {
// We expect 3 parts, but limit to 4 to allow detection of invalid values.
parts := strings.SplitN(mapping, ":", 4)