Merge pull request #45620 from thaJeztah/24.0_backport_update_go_runc_v1.1.0

[24.0 backport] vendor: github.com/containerd/go-runc v1.1.0
This commit is contained in:
Bjorn Neergaard 2023-05-25 13:14:38 -06:00 committed by GitHub
commit 37bc639704
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 414 additions and 232 deletions

View file

@ -118,7 +118,7 @@ require (
github.com/containerd/cgroups v1.0.4 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/containerd/go-cni v1.1.6 // indirect
github.com/containerd/go-runc v1.0.0 // indirect
github.com/containerd/go-runc v1.1.0 // indirect
github.com/containerd/nydus-snapshotter v0.3.1 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.13.0 // indirect
github.com/containerd/ttrpc v1.1.1 // indirect

View file

@ -397,8 +397,9 @@ github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH
github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.1.0 h1:OX4f+/i2y5sUT7LhmcJH7GYrjjhHa1QI4e8yO0gGleA=
github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U=
github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=

20
vendor/github.com/containerd/go-runc/.golangci.yml generated vendored Normal file
View file

@ -0,0 +1,20 @@
linters:
enable:
- gofmt
- goimports
- ineffassign
- misspell
- revive
- staticcheck
- unconvert
- unused
- vet
disable:
- errcheck
issues:
include:
- EXC0002
run:
timeout: 2m

View file

@ -1,21 +0,0 @@
language: go
go:
- 1.13.x
- 1.14.x
- 1.15.x
install:
- go get -t ./...
- go get -u github.com/vbatts/git-validation
- go get -u github.com/kunalkushwaha/ltag
before_script:
- pushd ..; git clone https://github.com/containerd/project; popd
script:
- DCO_VERBOSITY=-q ../project/script/validate/dco
- ../project/script/validate/fileheader ../project/
- go test -v -race -covermode=atomic -coverprofile=coverage.txt ./...
after_success:
- bash <(curl -s https://codecov.io/bash)

View file

@ -1,7 +1,7 @@
# go-runc
[![Build Status](https://travis-ci.org/containerd/go-runc.svg?branch=master)](https://travis-ci.org/containerd/go-runc)
[![codecov](https://codecov.io/gh/containerd/go-runc/branch/master/graph/badge.svg)](https://codecov.io/gh/containerd/go-runc)
[![Build Status](https://github.com/containerd/go-runc/workflows/CI/badge.svg)](https://github.com/containerd/go-runc/actions?query=workflow%3ACI)
[![codecov](https://codecov.io/gh/containerd/go-runc/branch/main/graph/badge.svg)](https://codecov.io/gh/containerd/go-runc)
This is a package for consuming the [runc](https://github.com/opencontainers/runc) binary in your Go applications.
It tries to expose all the settings and features of the runc CLI. If there is something missing then add it, its opensource!
@ -18,8 +18,8 @@ Docs can be found at [godoc.org](https://godoc.org/github.com/containerd/go-runc
The go-runc is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
* [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

View file

@ -1,4 +1,4 @@
// +build !linux
//go:build !linux
/*
Copyright The containerd Authors.

View file

@ -1,4 +1,4 @@
// +build !windows
//go:build !windows
/*
Copyright The containerd Authors.
@ -20,7 +20,6 @@ package runc
import (
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
@ -53,7 +52,7 @@ func NewConsoleSocket(path string) (*Socket, error) {
// On Close(), the socket is deleted
func NewTempConsoleSocket() (*Socket, error) {
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
dir, err := ioutil.TempDir(runtimeDir, "pty")
dir, err := os.MkdirTemp(runtimeDir, "pty")
if err != nil {
return nil, err
}
@ -70,7 +69,7 @@ func NewTempConsoleSocket() (*Socket, error) {
return nil, err
}
if runtimeDir != "" {
if err := os.Chmod(abs, 0755|os.ModeSticky); err != nil {
if err := os.Chmod(abs, 0o755|os.ModeSticky); err != nil {
return nil, err
}
}
@ -96,7 +95,7 @@ func (c *Socket) Path() string {
// locally (it is sent as non-auxiliary data in the same payload).
func recvFd(socket *net.UnixConn) (*os.File, error) {
const MaxNameLen = 4096
var oobSpace = unix.CmsgSpace(4)
oobSpace := unix.CmsgSpace(4)
name := make([]byte, MaxNameLen)
oob := make([]byte, oobSpace)

View file

@ -16,6 +16,7 @@
package runc
// Event is a struct to pass runc event information
type Event struct {
// Type are the event type generated by runc
// If the type is "error" then check the Err field on the event for
@ -27,20 +28,23 @@ type Event struct {
Err error `json:"-"`
}
// Stats is statistical information from the runc process
type Stats struct {
Cpu Cpu `json:"cpu"`
Cpu Cpu `json:"cpu"` //revive:disable
Memory Memory `json:"memory"`
Pids Pids `json:"pids"`
Blkio Blkio `json:"blkio"`
Hugetlb map[string]Hugetlb `json:"hugetlb"`
}
// Hugetlb represents the detailed hugetlb component of the statistics data
type Hugetlb struct {
Usage uint64 `json:"usage,omitempty"`
Max uint64 `json:"max,omitempty"`
Failcnt uint64 `json:"failcnt"`
}
// BlkioEntry represents a block IO entry in the IO stats
type BlkioEntry struct {
Major uint64 `json:"major,omitempty"`
Minor uint64 `json:"minor,omitempty"`
@ -48,6 +52,7 @@ type BlkioEntry struct {
Value uint64 `json:"value,omitempty"`
}
// Blkio represents the statistical information from block IO devices
type Blkio struct {
IoServiceBytesRecursive []BlkioEntry `json:"ioServiceBytesRecursive,omitempty"`
IoServicedRecursive []BlkioEntry `json:"ioServicedRecursive,omitempty"`
@ -59,17 +64,22 @@ type Blkio struct {
SectorsRecursive []BlkioEntry `json:"sectorsRecursive,omitempty"`
}
// Pids represents the process ID information
type Pids struct {
Current uint64 `json:"current,omitempty"`
Limit uint64 `json:"limit,omitempty"`
}
// Throttling represents the throttling statistics
type Throttling struct {
Periods uint64 `json:"periods,omitempty"`
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
ThrottledTime uint64 `json:"throttledTime,omitempty"`
}
// CpuUsage represents the CPU usage statistics
//
//revive:disable-next-line
type CpuUsage struct {
// Units: nanoseconds.
Total uint64 `json:"total,omitempty"`
@ -78,11 +88,15 @@ type CpuUsage struct {
User uint64 `json:"user"`
}
// Cpu represents the CPU usage and throttling statistics
//
//revive:disable-next-line
type Cpu struct {
Usage CpuUsage `json:"usage,omitempty"`
Throttling Throttling `json:"throttling,omitempty"`
}
// MemoryEntry represents an item in the memory use/statistics
type MemoryEntry struct {
Limit uint64 `json:"limit"`
Usage uint64 `json:"usage,omitempty"`
@ -90,6 +104,7 @@ type MemoryEntry struct {
Failcnt uint64 `json:"failcnt"`
}
// Memory represents the collection of memory statistics from the process
type Memory struct {
Cache uint64 `json:"cache,omitempty"`
Usage MemoryEntry `json:"usage,omitempty"`

View file

@ -22,6 +22,7 @@ import (
"os/exec"
)
// IO is the terminal IO interface
type IO interface {
io.Closer
Stdin() io.WriteCloser
@ -30,6 +31,7 @@ type IO interface {
Set(*exec.Cmd)
}
// StartCloser is an interface to handle IO closure after start
type StartCloser interface {
CloseAfterStart() error
}
@ -76,6 +78,12 @@ func (p *pipe) Close() error {
return err
}
// NewPipeIO creates pipe pairs to be used with runc. It is not implemented
// on Windows.
func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
return newPipeIO(uid, gid, opts...)
}
type pipeIO struct {
in *pipe
out *pipe
@ -144,12 +152,12 @@ func (i *pipeIO) Set(cmd *exec.Cmd) {
}
}
// NewSTDIO returns I/O setup for standard OS in/out/err usage
func NewSTDIO() (IO, error) {
return &stdio{}, nil
}
type stdio struct {
}
type stdio struct{}
func (s *stdio) Close() error {
return nil

View file

@ -1,4 +1,4 @@
// +build !windows
//go:build !windows
/*
Copyright The containerd Authors.
@ -19,14 +19,15 @@
package runc
import (
"github.com/pkg/errors"
"fmt"
"runtime"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
"runtime"
)
// NewPipeIO creates pipe pairs to be used with runc
func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
// newPipeIO creates pipe pairs to be used with runc
func newPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
option := defaultIOOption()
for _, o := range opts {
o(option)
@ -54,7 +55,7 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
if runtime.GOOS == "darwin" {
logrus.WithError(err).Debug("failed to chown stdin, ignored")
} else {
return nil, errors.Wrap(err, "failed to chown stdin")
return nil, fmt.Errorf("failed to chown stdin: %w", err)
}
}
}
@ -69,7 +70,7 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
if runtime.GOOS == "darwin" {
logrus.WithError(err).Debug("failed to chown stdout, ignored")
} else {
return nil, errors.Wrap(err, "failed to chown stdout")
return nil, fmt.Errorf("failed to chown stdout: %w", err)
}
}
}
@ -84,7 +85,7 @@ func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
if runtime.GOOS == "darwin" {
logrus.WithError(err).Debug("failed to chown stderr, ignored")
} else {
return nil, errors.Wrap(err, "failed to chown stderr")
return nil, fmt.Errorf("failed to chown stderr: %w", err)
}
}
}

View file

@ -1,4 +1,4 @@
// +build windows
//go:build windows
/*
Copyright The containerd Authors.
@ -18,45 +18,8 @@
package runc
// NewPipeIO creates pipe pairs to be used with runc
func NewPipeIO(opts ...IOOpt) (i IO, err error) {
option := defaultIOOption()
for _, o := range opts {
o(option)
}
var (
pipes []*pipe
stdin, stdout, stderr *pipe
)
// cleanup in case of an error
defer func() {
if err != nil {
for _, p := range pipes {
p.Close()
}
}
}()
if option.OpenStdin {
if stdin, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdin)
}
if option.OpenStdout {
if stdout, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stdout)
}
if option.OpenStderr {
if stderr, err = newPipe(); err != nil {
return nil, err
}
pipes = append(pipes, stderr)
}
return &pipeIO{
in: stdin,
out: stdout,
err: stderr,
}, nil
import "errors"
func newPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) {
return nil, errors.New("not implemented on Windows")
}

View file

@ -18,32 +18,37 @@ package runc
import (
"os/exec"
"runtime"
"syscall"
"time"
)
// Monitor is the default ProcessMonitor for handling runc process exit
var Monitor ProcessMonitor = &defaultMonitor{}
// Exit holds the exit information from a process
type Exit struct {
Timestamp time.Time
Pid int
Status int
}
// ProcessMonitor is an interface for process monitoring
// ProcessMonitor is an interface for process monitoring.
//
// It allows daemons using go-runc to have a SIGCHLD handler
// to handle exits without introducing races between the handler
// and go's exec.Cmd
// These methods should match the methods exposed by exec.Cmd to provide
// a consistent experience for the caller
// and go's exec.Cmd.
//
// ProcessMonitor also provides a StartLocked method which is similar to
// Start, but locks the goroutine used to start the process to an OS thread
// (for example: when Pdeathsig is set).
type ProcessMonitor interface {
Start(*exec.Cmd) (chan Exit, error)
StartLocked(*exec.Cmd) (chan Exit, error)
Wait(*exec.Cmd, chan Exit) (int, error)
}
type defaultMonitor struct {
}
type defaultMonitor struct{}
func (m *defaultMonitor) Start(c *exec.Cmd) (chan Exit, error) {
if err := c.Start(); err != nil {
@ -70,6 +75,43 @@ func (m *defaultMonitor) Start(c *exec.Cmd) (chan Exit, error) {
return ec, nil
}
// StartLocked is like Start, but locks the goroutine used to start the process to
// the OS thread for use-cases where the parent thread matters to the child process
// (for example: when Pdeathsig is set).
func (m *defaultMonitor) StartLocked(c *exec.Cmd) (chan Exit, error) {
started := make(chan error)
ec := make(chan Exit, 1)
go func() {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := c.Start(); err != nil {
started <- err
return
}
close(started)
var status int
if err := c.Wait(); err != nil {
status = 255
if exitErr, ok := err.(*exec.ExitError); ok {
if ws, ok := exitErr.Sys().(syscall.WaitStatus); ok {
status = ws.ExitStatus()
}
}
}
ec <- Exit{
Timestamp: time.Now(),
Pid: c.Process.Pid,
Status: status,
}
close(ec)
}()
if err := <-started; err != nil {
return nil, err
}
return ec, nil
}
func (m *defaultMonitor) Wait(c *exec.Cmd, ec chan Exit) (int, error) {
e := <-ec
return e.Status, nil

View file

@ -23,21 +23,22 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-spec/specs-go/features"
)
// Format is the type of log formatting options avaliable
// Format is the type of log formatting options available
type Format string
// TopBody represents the structured data of the full ps output
// TopResults represents the structured data of the full ps output
type TopResults struct {
// Processes running in the container, where each is process is an array of values corresponding to the headers
Processes [][]string `json:"Processes"`
@ -48,15 +49,53 @@ type TopResults struct {
const (
none Format = ""
// JSON represents the JSON format
JSON Format = "json"
// Text represents plain text format
Text Format = "text"
// DefaultCommand is the default command for Runc
DefaultCommand = "runc"
)
// DefaultCommand is the default command for Runc
var DefaultCommand = "runc"
// Runc is the client to the runc cli
type Runc struct {
// Command overrides the name of the runc binary. If empty, DefaultCommand
// is used.
Command string
Root string
Debug bool
Log string
LogFormat Format
// PdeathSignal sets a signal the child process will receive when the
// parent dies.
//
// When Pdeathsig is set, command invocations will call runtime.LockOSThread
// to prevent OS thread termination from spuriously triggering the
// signal. See https://github.com/golang/go/issues/27505 and
// https://github.com/golang/go/blob/126c22a09824a7b52c019ed9a1d198b4e7781676/src/syscall/exec_linux.go#L48-L51
//
// A program with GOMAXPROCS=1 might hang because of the use of
// runtime.LockOSThread. Callers should ensure they retain at least one
// unlocked thread.
PdeathSignal syscall.Signal // using syscall.Signal to allow compilation on non-unix (unix.Syscall is an alias for syscall.Signal)
Setpgid bool
// Criu sets the path to the criu binary used for checkpoint and restore.
//
// Deprecated: runc option --criu is now ignored (with a warning), and the
// option will be removed entirely in a future release. Users who need a non-
// standard criu binary should rely on the standard way of looking up binaries
// in $PATH.
Criu string
SystemdCgroup bool
Rootless *bool // nil stands for "auto"
ExtraArgs []string
}
// List returns all containers created inside the provided runc root directory
func (r *Runc) List(context context.Context) ([]*Container, error) {
data, err := cmdOutput(r.command(context, "list", "--format=json"), false, nil)
data, err := r.cmdOutput(r.command(context, "list", "--format=json"), false, nil)
defer putBuf(data)
if err != nil {
return nil, err
@ -70,7 +109,7 @@ func (r *Runc) List(context context.Context) ([]*Container, error) {
// State returns the state for the container provided by id
func (r *Runc) State(context context.Context, id string) (*Container, error) {
data, err := cmdOutput(r.command(context, "state", id), true, nil)
data, err := r.cmdOutput(r.command(context, "state", id), true, nil)
defer putBuf(data)
if err != nil {
return nil, fmt.Errorf("%s: %s", err, data.String())
@ -82,10 +121,12 @@ func (r *Runc) State(context context.Context, id string) (*Container, error) {
return &c, nil
}
// ConsoleSocket handles the path of the socket for console access
type ConsoleSocket interface {
Path() string
}
// CreateOpts holds all the options information for calling runc with supported options
type CreateOpts struct {
IO
// PidFile is a path to where a pid file should be created
@ -96,6 +137,7 @@ type CreateOpts struct {
NoNewKeyring bool
ExtraFiles []*os.File
Started chan<- int
ExtraArgs []string
}
func (o *CreateOpts) args() (out []string, err error) {
@ -121,38 +163,50 @@ func (o *CreateOpts) args() (out []string, err error) {
if o.ExtraFiles != nil {
out = append(out, "--preserve-fds", strconv.Itoa(len(o.ExtraFiles)))
}
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out, nil
}
func (r *Runc) startCommand(cmd *exec.Cmd) (chan Exit, error) {
if r.PdeathSignal != 0 {
return Monitor.StartLocked(cmd)
}
return Monitor.Start(cmd)
}
// Create creates a new container and returns its pid if it was created successfully
func (r *Runc) Create(context context.Context, id, bundle string, opts *CreateOpts) error {
args := []string{"create", "--bundle", bundle}
if opts != nil {
oargs, err := opts.args()
if err != nil {
return err
}
args = append(args, oargs...)
if opts == nil {
opts = &CreateOpts{}
}
oargs, err := opts.args()
if err != nil {
return err
}
args = append(args, oargs...)
cmd := r.command(context, append(args, id)...)
if opts != nil && opts.IO != nil {
if opts.IO != nil {
opts.Set(cmd)
}
cmd.ExtraFiles = opts.ExtraFiles
if cmd.Stdout == nil && cmd.Stderr == nil {
data, err := cmdOutput(cmd, true, nil)
data, err := r.cmdOutput(cmd, true, nil)
defer putBuf(data)
if err != nil {
return fmt.Errorf("%s: %s", err, data.String())
}
return nil
}
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
return err
}
if opts != nil && opts.IO != nil {
if opts.IO != nil {
if c, ok := opts.IO.(StartCloser); ok {
if err := c.CloseAfterStart(); err != nil {
return err
@ -171,12 +225,14 @@ func (r *Runc) Start(context context.Context, id string) error {
return r.runOrError(r.command(context, "start", id))
}
// ExecOpts holds optional settings when starting an exec process with runc
type ExecOpts struct {
IO
PidFile string
ConsoleSocket ConsoleSocket
Detach bool
Started chan<- int
ExtraArgs []string
}
func (o *ExecOpts) args() (out []string, err error) {
@ -193,16 +249,22 @@ func (o *ExecOpts) args() (out []string, err error) {
}
out = append(out, "--pid-file", abs)
}
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out, nil
}
// Exec executes an additional process inside the container based on a full
// OCI Process specification
func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts *ExecOpts) error {
if opts == nil {
opts = &ExecOpts{}
}
if opts.Started != nil {
defer close(opts.Started)
}
f, err := ioutil.TempFile(os.Getenv("XDG_RUNTIME_DIR"), "runc-process")
f, err := os.CreateTemp(os.Getenv("XDG_RUNTIME_DIR"), "runc-process")
if err != nil {
return err
}
@ -213,33 +275,31 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts
return err
}
args := []string{"exec", "--process", f.Name()}
if opts != nil {
oargs, err := opts.args()
if err != nil {
return err
}
args = append(args, oargs...)
oargs, err := opts.args()
if err != nil {
return err
}
args = append(args, oargs...)
cmd := r.command(context, append(args, id)...)
if opts != nil && opts.IO != nil {
if opts.IO != nil {
opts.Set(cmd)
}
if cmd.Stdout == nil && cmd.Stderr == nil {
data, err := cmdOutput(cmd, true, opts.Started)
data, err := r.cmdOutput(cmd, true, opts.Started)
defer putBuf(data)
if err != nil {
return fmt.Errorf("%w: %s", err, data.String())
}
return nil
}
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
return err
}
if opts.Started != nil {
opts.Started <- cmd.Process.Pid
}
if opts != nil && opts.IO != nil {
if opts.IO != nil {
if c, ok := opts.IO.(StartCloser); ok {
if err := c.CloseAfterStart(); err != nil {
return err
@ -256,22 +316,24 @@ func (r *Runc) Exec(context context.Context, id string, spec specs.Process, opts
// Run runs the create, start, delete lifecycle of the container
// and returns its exit status after it has exited
func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts) (int, error) {
if opts == nil {
opts = &CreateOpts{}
}
if opts.Started != nil {
defer close(opts.Started)
}
args := []string{"run", "--bundle", bundle}
if opts != nil {
oargs, err := opts.args()
if err != nil {
return -1, err
}
args = append(args, oargs...)
oargs, err := opts.args()
if err != nil {
return -1, err
}
args = append(args, oargs...)
cmd := r.command(context, append(args, id)...)
if opts != nil && opts.IO != nil {
if opts.IO != nil {
opts.Set(cmd)
}
ec, err := Monitor.Start(cmd)
cmd.ExtraFiles = opts.ExtraFiles
ec, err := r.startCommand(cmd)
if err != nil {
return -1, err
}
@ -285,14 +347,19 @@ func (r *Runc) Run(context context.Context, id, bundle string, opts *CreateOpts)
return status, err
}
// DeleteOpts holds the deletion options for calling `runc delete`
type DeleteOpts struct {
Force bool
Force bool
ExtraArgs []string
}
func (o *DeleteOpts) args() (out []string) {
if o.Force {
out = append(out, "--force")
}
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out
}
@ -307,13 +374,17 @@ func (r *Runc) Delete(context context.Context, id string, opts *DeleteOpts) erro
// KillOpts specifies options for killing a container and its processes
type KillOpts struct {
All bool
All bool
ExtraArgs []string
}
func (o *KillOpts) args() (out []string) {
if o.All {
out = append(out, "--all")
}
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out
}
@ -335,7 +406,7 @@ func (r *Runc) Stats(context context.Context, id string) (*Stats, error) {
if err != nil {
return nil, err
}
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
return nil, err
}
@ -357,7 +428,7 @@ func (r *Runc) Events(context context.Context, id string, interval time.Duration
if err != nil {
return nil, err
}
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
rd.Close()
return nil, err
@ -401,7 +472,7 @@ func (r *Runc) Resume(context context.Context, id string) error {
// Ps lists all the processes inside the container returning their pids
func (r *Runc) Ps(context context.Context, id string) ([]int, error) {
data, err := cmdOutput(r.command(context, "ps", "--format", "json", id), true, nil)
data, err := r.cmdOutput(r.command(context, "ps", "--format", "json", id), true, nil)
defer putBuf(data)
if err != nil {
return nil, fmt.Errorf("%s: %s", err, data.String())
@ -415,7 +486,7 @@ func (r *Runc) Ps(context context.Context, id string) ([]int, error) {
// Top lists all the processes inside the container returning the full ps data
func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopResults, error) {
data, err := cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true, nil)
data, err := r.cmdOutput(r.command(context, "ps", "--format", "table", id, psOptions), true, nil)
defer putBuf(data)
if err != nil {
return nil, fmt.Errorf("%s: %s", err, data.String())
@ -428,6 +499,7 @@ func (r *Runc) Top(context context.Context, id string, psOptions string) (*TopRe
return topResults, nil
}
// CheckpointOpts holds the options for performing a criu checkpoint using runc
type CheckpointOpts struct {
// ImagePath is the path for saving the criu image file
ImagePath string
@ -454,13 +526,18 @@ type CheckpointOpts struct {
LazyPages bool
// StatusFile is the file criu writes \0 to once lazy-pages is ready
StatusFile *os.File
ExtraArgs []string
}
// CgroupMode defines the cgroup mode used for checkpointing
type CgroupMode string
const (
Soft CgroupMode = "soft"
Full CgroupMode = "full"
// Soft is the "soft" cgroup mode
Soft CgroupMode = "soft"
// Full is the "full" cgroup mode
Full CgroupMode = "full"
// Strict is the "strict" cgroup mode
Strict CgroupMode = "strict"
)
@ -498,9 +575,13 @@ func (o *CheckpointOpts) args() (out []string) {
if o.LazyPages {
out = append(out, "--lazy-pages")
}
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out
}
// CheckpointAction represents specific actions executed during checkpoint/restore
type CheckpointAction func([]string) []string
// LeaveRunning keeps the container running after the checkpoint has been completed
@ -535,6 +616,7 @@ func (r *Runc) Checkpoint(context context.Context, id string, opts *CheckpointOp
return r.runOrError(cmd)
}
// RestoreOpts holds the options for performing a criu restore using runc
type RestoreOpts struct {
CheckpointOpts
IO
@ -544,6 +626,7 @@ type RestoreOpts struct {
NoSubreaper bool
NoPivot bool
ConsoleSocket ConsoleSocket
ExtraArgs []string
}
func (o *RestoreOpts) args() ([]string, error) {
@ -567,6 +650,9 @@ func (o *RestoreOpts) args() ([]string, error) {
if o.NoSubreaper {
out = append(out, "-no-subreaper")
}
if len(o.ExtraArgs) > 0 {
out = append(out, o.ExtraArgs...)
}
return out, nil
}
@ -585,7 +671,7 @@ func (r *Runc) Restore(context context.Context, id, bundle string, opts *Restore
if opts != nil && opts.IO != nil {
opts.Set(cmd)
}
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
return -1, err
}
@ -611,14 +697,16 @@ func (r *Runc) Update(context context.Context, id string, resources *specs.Linux
if err := json.NewEncoder(buf).Encode(resources); err != nil {
return err
}
args := []string{"update", "--resources", "-", id}
args := []string{"update", "--resources=-", id}
cmd := r.command(context, args...)
cmd.Stdin = buf
return r.runOrError(cmd)
}
// ErrParseRuncVersion is used when the runc version can't be parsed
var ErrParseRuncVersion = errors.New("unable to parse runc version")
// Version represents the runc version information
type Version struct {
Runc string
Commit string
@ -627,7 +715,7 @@ type Version struct {
// Version returns the runc and runtime-spec versions
func (r *Runc) Version(context context.Context) (Version, error) {
data, err := cmdOutput(r.command(context, "--version"), false, nil)
data, err := r.cmdOutput(r.command(context, "--version"), false, nil)
defer putBuf(data)
if err != nil {
return Version{}, err
@ -657,6 +745,26 @@ func parseVersion(data []byte) (Version, error) {
return v, nil
}
// Features shows the features implemented by the runtime.
//
// Availability:
//
// - runc: supported since runc v1.1.0
// - crun: https://github.com/containers/crun/issues/1177
// - youki: https://github.com/containers/youki/issues/815
func (r *Runc) Features(context context.Context) (*features.Features, error) {
data, err := r.cmdOutput(r.command(context, "features"), false, nil)
defer putBuf(data)
if err != nil {
return nil, err
}
var feat features.Features
if err := json.Unmarshal(data.Bytes(), &feat); err != nil {
return nil, err
}
return &feat, nil
}
func (r *Runc) args() (out []string) {
if r.Root != "" {
out = append(out, "--root", r.Root)
@ -670,9 +778,6 @@ func (r *Runc) args() (out []string) {
if r.LogFormat != none {
out = append(out, "--log-format", string(r.LogFormat))
}
if r.Criu != "" {
out = append(out, "--criu", r.Criu)
}
if r.SystemdCgroup {
out = append(out, "--systemd-cgroup")
}
@ -680,6 +785,9 @@ func (r *Runc) args() (out []string) {
// nil stands for "auto" (differs from explicit "false")
out = append(out, "--rootless="+strconv.FormatBool(*r.Rootless))
}
if len(r.ExtraArgs) > 0 {
out = append(out, r.ExtraArgs...)
}
return out
}
@ -689,7 +797,7 @@ func (r *Runc) args() (out []string) {
// <stderr>
func (r *Runc) runOrError(cmd *exec.Cmd) error {
if cmd.Stdout != nil || cmd.Stderr != nil {
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
return err
}
@ -699,7 +807,7 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error {
}
return err
}
data, err := cmdOutput(cmd, true, nil)
data, err := r.cmdOutput(cmd, true, nil)
defer putBuf(data)
if err != nil {
return fmt.Errorf("%s: %s", err, data.String())
@ -709,14 +817,14 @@ func (r *Runc) runOrError(cmd *exec.Cmd) error {
// callers of cmdOutput are expected to call putBuf on the returned Buffer
// to ensure it is released back to the shared pool after use.
func cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer, error) {
func (r *Runc) cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer, error) {
b := getBuf()
cmd.Stdout = b
if combined {
cmd.Stderr = b
}
ec, err := Monitor.Start(cmd)
ec, err := r.startCommand(cmd)
if err != nil {
return nil, err
}
@ -732,6 +840,7 @@ func cmdOutput(cmd *exec.Cmd, combined bool, started chan<- int) (*bytes.Buffer,
return b, err
}
// ExitError holds the status return code when a process exits with an error code
type ExitError struct {
Status int
}

View file

@ -1,38 +0,0 @@
//+build !windows
/*
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 runc
import (
"golang.org/x/sys/unix"
)
// Runc is the client to the runc cli
type Runc struct {
//If command is empty, DefaultCommand is used
Command string
Root string
Debug bool
Log string
LogFormat Format
PdeathSignal unix.Signal
Setpgid bool
Criu string
SystemdCgroup bool
Rootless *bool // nil stands for "auto"
}

View file

@ -1,31 +0,0 @@
/*
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 runc
// Runc is the client to the runc cli
type Runc struct {
//If command is empty, DefaultCommand is used
Command string
Root string
Debug bool
Log string
LogFormat Format
Setpgid bool
Criu string
SystemdCgroup bool
Rootless *bool // nil stands for "auto"
}

View file

@ -18,34 +18,22 @@ package runc
import (
"bytes"
"io/ioutil"
"os"
"strconv"
"strings"
"sync"
"syscall"
)
// ReadPidFile reads the pid file at the provided path and returns
// the pid or an error if the read and conversion is unsuccessful
func ReadPidFile(path string) (int, error) {
data, err := ioutil.ReadFile(path)
data, err := os.ReadFile(path)
if err != nil {
return -1, err
}
return strconv.Atoi(string(data))
}
const exitSignalOffset = 128
// exitStatus returns the correct exit status for a process based on if it
// was signaled or exited cleanly
func exitStatus(status syscall.WaitStatus) int {
if status.Signaled() {
return exitSignalOffset + int(status.Signal())
}
return status.ExitStatus()
}
var bytesBufferPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(nil)

View file

@ -0,0 +1,125 @@
// Package features provides the Features struct.
package features
// Features represents the supported features of the runtime.
type Features struct {
// OCIVersionMin is the minimum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.0".
OCIVersionMin string `json:"ociVersionMin,omitempty"`
// OCIVersionMax is the maximum OCI Runtime Spec version recognized by the runtime, e.g., "1.0.2-dev".
OCIVersionMax string `json:"ociVersionMax,omitempty"`
// Hooks is the list of the recognized hook names, e.g., "createRuntime".
// Nil value means "unknown", not "no support for any hook".
Hooks []string `json:"hooks,omitempty"`
// MountOptions is the list of the recognized mount options, e.g., "ro".
// Nil value means "unknown", not "no support for any mount option".
// This list does not contain filesystem-specific options passed to mount(2) syscall as (const void *).
MountOptions []string `json:"mountOptions,omitempty"`
// Linux is specific to Linux.
Linux *Linux `json:"linux,omitempty"`
// Annotations contains implementation-specific annotation strings,
// such as the implementation version, and third-party extensions.
Annotations map[string]string `json:"annotations,omitempty"`
}
// Linux is specific to Linux.
type Linux struct {
// Namespaces is the list of the recognized namespaces, e.g., "mount".
// Nil value means "unknown", not "no support for any namespace".
Namespaces []string `json:"namespaces,omitempty"`
// Capabilities is the list of the recognized capabilities , e.g., "CAP_SYS_ADMIN".
// Nil value means "unknown", not "no support for any capability".
Capabilities []string `json:"capabilities,omitempty"`
Cgroup *Cgroup `json:"cgroup,omitempty"`
Seccomp *Seccomp `json:"seccomp,omitempty"`
Apparmor *Apparmor `json:"apparmor,omitempty"`
Selinux *Selinux `json:"selinux,omitempty"`
IntelRdt *IntelRdt `json:"intelRdt,omitempty"`
}
// Cgroup represents the "cgroup" field.
type Cgroup struct {
// V1 represents whether Cgroup v1 support is compiled in.
// Unrelated to whether the host uses cgroup v1 or not.
// Nil value means "unknown", not "false".
V1 *bool `json:"v1,omitempty"`
// V2 represents whether Cgroup v2 support is compiled in.
// Unrelated to whether the host uses cgroup v2 or not.
// Nil value means "unknown", not "false".
V2 *bool `json:"v2,omitempty"`
// Systemd represents whether systemd-cgroup support is compiled in.
// Unrelated to whether the host uses systemd or not.
// Nil value means "unknown", not "false".
Systemd *bool `json:"systemd,omitempty"`
// SystemdUser represents whether user-scoped systemd-cgroup support is compiled in.
// Unrelated to whether the host uses systemd or not.
// Nil value means "unknown", not "false".
SystemdUser *bool `json:"systemdUser,omitempty"`
// Rdma represents whether RDMA cgroup support is compiled in.
// Unrelated to whether the host supports RDMA or not.
// Nil value means "unknown", not "false".
Rdma *bool `json:"rdma,omitempty"`
}
// Seccomp represents the "seccomp" field.
type Seccomp struct {
// Enabled is true if seccomp support is compiled in.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
// Actions is the list of the recognized actions, e.g., "SCMP_ACT_NOTIFY".
// Nil value means "unknown", not "no support for any action".
Actions []string `json:"actions,omitempty"`
// Operators is the list of the recognized operators, e.g., "SCMP_CMP_NE".
// Nil value means "unknown", not "no support for any operator".
Operators []string `json:"operators,omitempty"`
// Archs is the list of the recognized archs, e.g., "SCMP_ARCH_X86_64".
// Nil value means "unknown", not "no support for any arch".
Archs []string `json:"archs,omitempty"`
// KnownFlags is the list of the recognized filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
// Nil value means "unknown", not "no flags are recognized".
KnownFlags []string `json:"knownFlags,omitempty"`
// SupportedFlags is the list of the supported filter flags, e.g., "SECCOMP_FILTER_FLAG_LOG".
// This list may be a subset of KnownFlags due to some flags
// not supported by the current kernel and/or libseccomp.
// Nil value means "unknown", not "no flags are supported".
SupportedFlags []string `json:"supportedFlags,omitempty"`
}
// Apparmor represents the "apparmor" field.
type Apparmor struct {
// Enabled is true if AppArmor support is compiled in.
// Unrelated to whether the host supports AppArmor or not.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
}
// Selinux represents the "selinux" field.
type Selinux struct {
// Enabled is true if SELinux support is compiled in.
// Unrelated to whether the host supports SELinux or not.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
}
// IntelRdt represents the "intelRdt" field.
type IntelRdt struct {
// Enabled is true if Intel RDT support is compiled in.
// Unrelated to whether the host supports Intel RDT or not.
// Nil value means "unknown", not "false".
Enabled *bool `json:"enabled,omitempty"`
}

5
vendor/modules.txt vendored
View file

@ -311,8 +311,8 @@ github.com/containerd/fifo
# github.com/containerd/go-cni v1.1.6
## explicit; go 1.17
github.com/containerd/go-cni
# github.com/containerd/go-runc v1.0.0
## explicit; go 1.13
# github.com/containerd/go-runc v1.1.0
## explicit; go 1.18
github.com/containerd/go-runc
# github.com/containerd/nydus-snapshotter v0.3.1
## explicit; go 1.17
@ -849,6 +849,7 @@ github.com/opencontainers/runc/libcontainer/userns
# github.com/opencontainers/runtime-spec v1.1.0-rc.2
## explicit
github.com/opencontainers/runtime-spec/specs-go
github.com/opencontainers/runtime-spec/specs-go/features
# github.com/opencontainers/selinux v1.11.0
## explicit; go 1.19
github.com/opencontainers/selinux/go-selinux