Merge pull request #8262 from mxpv/v1

🪦 Remove `io.containerd.runtime.v1.linux` and `io.containerd.runc.v1`
This commit is contained in:
Akihiro Suda 2023-03-16 09:56:54 +09:00 committed by GitHub
commit f558a3d598
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 132 additions and 9279 deletions

View File

@ -390,16 +390,9 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
runtime: runtime:
- io.containerd.runtime.v1.linux
- io.containerd.runc.v1
- io.containerd.runc.v2 - io.containerd.runc.v2
runc: [runc, crun] runc: [runc, crun]
enable_cri_sandboxes: ["", "sandboxed"] enable_cri_sandboxes: ["", "sandboxed"]
exclude:
- runtime: io.containerd.runc.v1
runc: crun
- runtime: io.containerd.runtime.v1.linux
runc: crun
env: env:
GOTEST: gotestsum -- GOTEST: gotestsum --

View File

@ -254,14 +254,6 @@ bin/gen-manpages: cmd/gen-manpages FORCE
@echo "$(WHALE) $@" @echo "$(WHALE) $@"
$(GO) build ${DEBUG_GO_GCFLAGS} ${GO_GCFLAGS} ${GO_BUILD_FLAGS} -o $@ ${GO_LDFLAGS} $(subst urfave_cli_no_docs,,${GO_TAGS}) ./cmd/gen-manpages $(GO) build ${DEBUG_GO_GCFLAGS} ${GO_GCFLAGS} ${GO_BUILD_FLAGS} -o $@ ${GO_LDFLAGS} $(subst urfave_cli_no_docs,,${GO_TAGS}) ./cmd/gen-manpages
bin/containerd-shim: cmd/containerd-shim FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220
@echo "$(WHALE) $@"
@CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim
bin/containerd-shim-runc-v1: cmd/containerd-shim-runc-v1 FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220
@echo "$(WHALE) $@"
@CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim-runc-v1
bin/containerd-shim-runc-v2: cmd/containerd-shim-runc-v2 FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220 bin/containerd-shim-runc-v2: cmd/containerd-shim-runc-v2 FORCE # set !cgo and omit pie for a static shim build: https://github.com/golang/go/issues/17789#issuecomment-258542220
@echo "$(WHALE) $@" @echo "$(WHALE) $@"
@CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim-runc-v2 @CGO_ENABLED=${SHIM_CGO_ENABLED} $(GO) build ${GO_BUILD_FLAGS} -o $@ ${SHIM_GO_LDFLAGS} ${GO_TAGS} ./cmd/containerd-shim-runc-v2
@ -400,7 +392,7 @@ clean: ## clean up binaries
clean-test: ## clean up debris from previously failed tests clean-test: ## clean up debris from previously failed tests
@echo "$(WHALE) $@" @echo "$(WHALE) $@"
$(eval containers=$(shell find /run/containerd/runc -mindepth 2 -maxdepth 3 -type d -exec basename {} \;)) $(eval containers=$(shell find /run/containerd/runc -mindepth 2 -maxdepth 3 -type d -exec basename {} \;))
$(shell pidof containerd containerd-shim runc | xargs -r -n 1 kill -9) $(shell pidof containerd runc | xargs -r -n 1 kill -9)
@( for container in $(containers); do \ @( for container in $(containers); do \
grep $$container /proc/self/mountinfo | while read -r mountpoint; do \ grep $$container /proc/self/mountinfo | while read -r mountpoint; do \
umount $$(echo $$mountpoint | awk '{print $$5}'); \ umount $$(echo $$mountpoint | awk '{print $$5}'); \

View File

@ -13,9 +13,6 @@
# limitations under the License. # limitations under the License.
#darwin specific settings
COMMANDS += containerd-shim
# amd64 supports go test -race # amd64 supports go test -race
ifeq ($(GOARCH),amd64) ifeq ($(GOARCH),amd64)
TESTFLAGS_RACE= -race TESTFLAGS_RACE= -race

View File

@ -14,7 +14,6 @@
#freebsd specific settings #freebsd specific settings
COMMANDS += containerd-shim
# amd64 supports go test -race # amd64 supports go test -race
ifeq ($(GOARCH),amd64) ifeq ($(GOARCH),amd64)

View File

@ -16,7 +16,7 @@
#linux specific settings #linux specific settings
WHALE="+" WHALE="+"
ONI="-" ONI="-"
COMMANDS += containerd-shim containerd-shim-runc-v1 containerd-shim-runc-v2 COMMANDS += containerd-shim-runc-v2
# check GOOS for cross compile builds # check GOOS for cross compile builds
ifeq ($(GOOS),linux) ifeq ($(GOOS),linux)

View File

@ -28,14 +28,6 @@ prefixes = [
] ]
generators = ["go", "go-grpc"] generators = ["go", "go-grpc"]
# Lock down runc config
[[descriptors]]
prefix = "github.com/containerd/containerd/runtime/linux/runctypes"
target = "runtime/linux/runctypes/next.pb.txt"
ignore_files = [
"google/protobuf/descriptor.proto",
]
[[descriptors]] [[descriptors]]
prefix = "github.com/containerd/containerd/runtime/v2/runc/options" prefix = "github.com/containerd/containerd/runtime/v2/runc/options"
target = "runtime/v2/runc/options/next.pb.txt" target = "runtime/v2/runc/options/next.pb.txt"

View File

@ -371,8 +371,8 @@ The deprecated features are shown in the following table:
| Component | Deprecation release | Target release for removal | Recommendation | | Component | Deprecation release | Target release for removal | Recommendation |
|----------------------------------------------------------------------------------|---------------------|----------------------------|------------------------------------------| |----------------------------------------------------------------------------------|---------------------|----------------------------|------------------------------------------|
| Runtime V1 API and implementation (`io.containerd.runtime.v1.linux`) | containerd v1.4 | containerd v2.0 | Use `io.containerd.runc.v2` | | Runtime V1 API and implementation (`io.containerd.runtime.v1.linux`) | containerd v1.4 | containerd v2.0 | Use `io.containerd.runc.v2` |
| Runc V1 implementation of Runtime V2 (`io.containerd.runc.v1`) | containerd v1.4 | containerd v2.0 | Use `io.containerd.runc.v2` | | Runc V1 implementation of Runtime V2 (`io.containerd.runc.v1`) | containerd v1.4 | containerd v2.0 | Use `io.containerd.runc.v2` |
| config.toml `version = 1` | containerd v1.5 | containerd v2.0 | Use config.toml `version = 2` | | config.toml `version = 1` | containerd v1.5 | containerd v2.0 | Use config.toml `version = 2` |
| Built-in `aufs` snapshotter | containerd v1.5 | containerd v2.0 ✅ | Use `overlayfs` snapshotter | | Built-in `aufs` snapshotter | containerd v1.5 | containerd v2.0 ✅ | Use `overlayfs` snapshotter |
| Container label `containerd.io/restart.logpath` | containerd v1.5 | containerd v2.0 ✅ | Use `containerd.io/restart.loguri` label | | Container label `containerd.io/restart.logpath` | containerd v1.5 | containerd v2.0 ✅ | Use `containerd.io/restart.loguri` label |
@ -385,12 +385,12 @@ The deprecated properties in [`config.toml`](./docs/cri/config.md) are shown in
| Property Group | Property | Deprecation release | Target release for removal | Recommendation | | Property Group | Property | Deprecation release | Target release for removal | Recommendation |
|----------------------------------------------------------------------|------------------------------|---------------------|----------------------------|-------------------------------------------------| |----------------------------------------------------------------------|------------------------------|---------------------|----------------------------|-------------------------------------------------|
|`[plugins."io.containerd.grpc.v1.cri"]` | `systemd_cgroup` | containerd v1.3 | containerd v2.0 | Use `SystemdCgroup` in runc options (see below) | |`[plugins."io.containerd.grpc.v1.cri"]` | `systemd_cgroup` | containerd v1.3 | containerd v2.0 | Use `SystemdCgroup` in runc options (see below) |
|`[plugins."io.containerd.grpc.v1.cri".cni]` | `conf_template` | containerd v1.? | containerd v2.0 | Create a CNI config in `/etc/cni/net.d` | |`[plugins."io.containerd.grpc.v1.cri".cni]` | `conf_template` | containerd v1.? | containerd v2.0 | Create a CNI config in `/etc/cni/net.d` |
|`[plugins."io.containerd.grpc.v1.cri".containerd]` | `untrusted_workload_runtime` | containerd v1.2 | containerd v2.0 | Create `untrusted` runtime in `runtimes` | |`[plugins."io.containerd.grpc.v1.cri".containerd]` | `untrusted_workload_runtime` | containerd v1.2 | containerd v2.0 | Create `untrusted` runtime in `runtimes` |
|`[plugins."io.containerd.grpc.v1.cri".containerd]` | `default_runtime` | containerd v1.3 | containerd v2.0 | Use `default_runtime_name` | |`[plugins."io.containerd.grpc.v1.cri".containerd]` | `default_runtime` | containerd v1.3 | containerd v2.0 | Use `default_runtime_name` |
|`[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.*]` | `runtime_engine` | containerd v1.3 | containerd v2.0 | Use runtime v2 | |`[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.*]` | `runtime_engine` | containerd v1.3 | containerd v2.0 | Use runtime v2 |
|`[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.*]` | `runtime_root` | containerd v1.3 | containerd v2.0 | Use `options.Root` | |`[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.*]` | `runtime_root` | containerd v1.3 | containerd v2.0 | Use `options.Root` |
|`[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.*.options]` | `CriuPath` | containerd v1.7 | containerd v2.0 | Set `$PATH` to the `criu` binary | |`[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.*.options]` | `CriuPath` | containerd v1.7 | containerd v2.0 | Set `$PATH` to the `criu` binary |
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `auths` | containerd v1.3 | containerd v2.0 | Use [`ImagePullSecrets`](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). See also [#8228](https://github.com/containerd/containerd/issues/8228). | |`[plugins."io.containerd.grpc.v1.cri".registry]` | `auths` | containerd v1.3 | containerd v2.0 | Use [`ImagePullSecrets`](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). See also [#8228](https://github.com/containerd/containerd/issues/8228). |
|`[plugins."io.containerd.grpc.v1.cri".registry]` | `configs` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) | |`[plugins."io.containerd.grpc.v1.cri".registry]` | `configs` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) |

View File

@ -818,23 +818,6 @@ func (c *Client) getSnapshotter(ctx context.Context, name string) (snapshots.Sna
return s, nil return s, nil
} }
// CheckRuntime returns true if the current runtime matches the expected
// runtime. Providing various parts of the runtime schema will match those
// parts of the expected runtime
func CheckRuntime(current, expected string) bool {
cp := strings.Split(current, ".")
l := len(cp)
for i, p := range strings.Split(expected, ".") {
if i > l {
return false
}
if p != cp[i] {
return false
}
}
return true
}
// GetSnapshotterSupportedPlatforms returns a platform matchers which represents the // GetSnapshotterSupportedPlatforms returns a platform matchers which represents the
// supported platforms for the given snapshotters // supported platforms for the given snapshotters
func (c *Client) GetSnapshotterSupportedPlatforms(ctx context.Context, snapshotterName string) (platforms.MatchComparer, error) { func (c *Client) GetSnapshotterSupportedPlatforms(ctx context.Context, snapshotterName string) (platforms.MatchComparer, error) {

View File

@ -1,28 +0,0 @@
//go: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 main
import (
v1 "github.com/containerd/containerd/runtime/v2/runc/v1"
"github.com/containerd/containerd/runtime/v2/shim"
)
func main() {
shim.Run("io.containerd.runc.v1", v1.New)
}

View File

@ -1,333 +0,0 @@
//go: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 main
import (
"bytes"
"context"
"errors"
"flag"
"fmt"
"io"
"net"
"os"
"os/signal"
"runtime"
"runtime/debug"
"strings"
"sync"
"syscall"
"time"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/process"
"github.com/containerd/containerd/protobuf"
"github.com/containerd/containerd/protobuf/proto"
ptypes "github.com/containerd/containerd/protobuf/types"
shimlog "github.com/containerd/containerd/runtime/v1"
"github.com/containerd/containerd/runtime/v1/shim"
shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/containerd/sys/reaper"
"github.com/containerd/containerd/version"
"github.com/containerd/ttrpc"
"github.com/sirupsen/logrus"
exec "golang.org/x/sys/execabs"
"golang.org/x/sys/unix"
)
var (
debugFlag bool
versionFlag bool
namespaceFlag string
socketFlag string
addressFlag string
workdirFlag string
runtimeRootFlag string
criuFlag string
systemdCgroupFlag bool
containerdBinaryFlag string
bufPool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(nil)
},
}
)
func parseFlags() {
flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs")
flag.BoolVar(&versionFlag, "v", false, "show the shim version and exit")
flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim")
flag.StringVar(&socketFlag, "socket", "", "socket path to serve")
flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd")
flag.StringVar(&workdirFlag, "workdir", "", "path used to storage large temporary data")
flag.StringVar(&runtimeRootFlag, "runtime-root", process.RuncRoot, "root directory for the runtime")
flag.StringVar(&criuFlag, "criu", "", "path to criu binary (deprecated: do not use)")
flag.BoolVar(&systemdCgroupFlag, "systemd-cgroup", false, "set runtime to use systemd-cgroup")
// currently, the `containerd publish` utility is embedded in the daemon binary.
// The daemon invokes `containerd-shim -containerd-binary ...` with its own os.Executable() path.
flag.StringVar(&containerdBinaryFlag, "containerd-binary", "containerd", "path to containerd binary (used for `containerd publish`)")
flag.Parse()
}
func setRuntime() {
debug.SetGCPercent(40)
go func() {
for range time.Tick(30 * time.Second) {
debug.FreeOSMemory()
}
}()
if os.Getenv("GOMAXPROCS") == "" {
// If GOMAXPROCS hasn't been set, we default to a value of 2 to reduce
// the number of Go stacks present in the shim.
runtime.GOMAXPROCS(2)
}
}
func main() {
parseFlags()
if versionFlag {
fmt.Println("containerd-shim")
fmt.Println(" Version: ", version.Version)
fmt.Println(" Revision:", version.Revision)
fmt.Println(" Go version:", version.GoVersion)
fmt.Println("")
return
}
setRuntime()
if debugFlag {
logrus.SetLevel(logrus.DebugLevel)
}
stdout, stderr, err := openStdioKeepAlivePipes(workdirFlag)
if err != nil {
fmt.Fprintf(os.Stderr, "containerd-shim: %s\n", err)
os.Exit(1)
}
defer func() {
stdout.Close()
stderr.Close()
}()
// redirect the following output into fifo to make sure that containerd
// still can read the log after restart
logrus.SetOutput(stdout)
if err := executeShim(); err != nil {
fmt.Fprintf(os.Stderr, "containerd-shim: %s\n", err)
os.Exit(1)
}
}
// If containerd server process dies, we need the shim to keep stdout/err reader
// FDs so that Linux does not SIGPIPE the shim process if it tries to use its end of
// these pipes.
func openStdioKeepAlivePipes(dir string) (io.ReadWriteCloser, io.ReadWriteCloser, error) {
background := context.Background()
keepStdoutAlive, err := shimlog.OpenShimStdoutLog(background, dir)
if err != nil {
return nil, nil, err
}
keepStderrAlive, err := shimlog.OpenShimStderrLog(background, dir)
if err != nil {
return nil, nil, err
}
return keepStdoutAlive, keepStderrAlive, nil
}
func executeShim() error {
// start handling signals as soon as possible so that things are properly reaped
// or if runtime exits before we hit the handler
signals, err := setupSignals()
if err != nil {
return err
}
dump := make(chan os.Signal, 32)
signal.Notify(dump, syscall.SIGUSR1)
path, err := os.Getwd()
if err != nil {
return err
}
server, err := newServer()
if err != nil {
return fmt.Errorf("failed creating server: %w", err)
}
sv, err := shim.NewService(
shim.Config{
Path: path,
Namespace: namespaceFlag,
WorkDir: workdirFlag,
SystemdCgroup: systemdCgroupFlag,
RuntimeRoot: runtimeRootFlag,
},
&remoteEventsPublisher{address: addressFlag},
)
if err != nil {
return err
}
logrus.Debug("registering ttrpc server")
shimapi.RegisterShimService(server, sv)
socket := socketFlag
if err := serve(context.Background(), server, socket); err != nil {
return err
}
logger := logrus.WithFields(logrus.Fields{
"pid": os.Getpid(),
"path": path,
"namespace": namespaceFlag,
})
go func() {
for range dump {
dumpStacks(logger)
}
}()
return handleSignals(logger, signals, server, sv)
}
// serve serves the ttrpc API over a unix socket at the provided path
// this function does not block
func serve(ctx context.Context, server *ttrpc.Server, path string) error {
var (
l net.Listener
err error
)
if path == "" {
f := os.NewFile(3, "socket")
l, err = net.FileListener(f)
f.Close()
path = "[inherited from parent]"
} else {
const (
abstractSocketPrefix = "\x00"
socketPathLimit = 106
)
p := strings.TrimPrefix(path, "unix://")
if len(p) == len(path) {
p = abstractSocketPrefix + p
}
if len(p) > socketPathLimit {
return fmt.Errorf("%q: unix socket path too long (> %d)", p, socketPathLimit)
}
l, err = net.Listen("unix", p)
}
if err != nil {
return err
}
logrus.WithField("socket", path).Debug("serving api on unix socket")
go func() {
defer l.Close()
if err := server.Serve(ctx, l); err != nil && !errors.Is(err, net.ErrClosed) {
logrus.WithError(err).Fatal("containerd-shim: ttrpc server failure")
}
}()
return nil
}
func handleSignals(logger *logrus.Entry, signals chan os.Signal, server *ttrpc.Server, sv *shim.Service) error {
var (
termOnce sync.Once
done = make(chan struct{})
)
for {
select {
case <-done:
return nil
case s := <-signals:
switch s {
case unix.SIGCHLD:
if err := reaper.Reap(); err != nil {
logger.WithError(err).Error("reap exit status")
}
case unix.SIGTERM, unix.SIGINT:
go termOnce.Do(func() {
ctx := context.TODO()
if err := server.Shutdown(ctx); err != nil {
logger.WithError(err).Error("failed to shutdown server")
}
// Ensure our child is dead if any
sv.Kill(ctx, &shimapi.KillRequest{
Signal: uint32(syscall.SIGKILL),
All: true,
})
sv.Delete(context.Background(), &ptypes.Empty{})
close(done)
})
case unix.SIGPIPE:
}
}
}
}
func dumpStacks(logger *logrus.Entry) {
var (
buf []byte
stackSize int
)
bufferLen := 16384
for stackSize == len(buf) {
buf = make([]byte, bufferLen)
stackSize = runtime.Stack(buf, true)
bufferLen *= 2
}
buf = buf[:stackSize]
logger.Infof("=== BEGIN goroutine stack dump ===\n%s\n=== END goroutine stack dump ===", buf)
}
type remoteEventsPublisher struct {
address string
}
func (l *remoteEventsPublisher) Publish(ctx context.Context, topic string, event events.Event) error {
ns, _ := namespaces.Namespace(ctx)
encoded, err := protobuf.MarshalAnyToProto(event)
if err != nil {
return err
}
data, err := proto.Marshal(encoded)
if err != nil {
return err
}
cmd := exec.CommandContext(ctx, containerdBinaryFlag, "--address", l.address, "publish", "--topic", topic, "--namespace", ns)
cmd.Stdin = bytes.NewReader(data)
b := bufPool.Get().(*bytes.Buffer)
defer func() {
b.Reset()
bufPool.Put(b)
}()
cmd.Stdout = b
cmd.Stderr = b
c, err := reaper.Default.Start(cmd)
if err != nil {
return err
}
status, err := reaper.Default.WaitTimeout(cmd, c, 30*time.Second)
if err != nil {
return fmt.Errorf("failed to publish event: %s: %w", b.String(), err)
}
if status != 0 {
return fmt.Errorf("failed to publish event: %s", b.String())
}
return nil
}

View File

@ -1,44 +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 main
import (
"os"
"os/signal"
"github.com/containerd/containerd/sys/reaper"
runc "github.com/containerd/go-runc"
"github.com/containerd/ttrpc"
)
// setupSignals creates a new signal handler for all signals and sets the shim as a
// sub-reaper so that the container processes are reparented
func setupSignals() (chan os.Signal, error) {
signals := make(chan os.Signal, 2048)
signal.Notify(signals)
// make sure runc is setup to use the monitor
// for waiting on processes
runc.Monitor = reaper.Default
return signals, nil
}
func newServer() (*ttrpc.Server, error) {
// for darwin, we omit the socket credentials because these syscalls are
// slightly different. since we don't have darwin support yet, this can be
// implemented later and the build can continue without issue.
return ttrpc.NewServer()
}

View File

@ -1,45 +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 main
import (
"os"
"os/signal"
"github.com/containerd/containerd/sys/reaper"
runc "github.com/containerd/go-runc"
"github.com/containerd/ttrpc"
)
// setupSignals creates a new signal handler for all signals and sets the shim as a
// sub-reaper so that the container processes are reparented
func setupSignals() (chan os.Signal, error) {
signals := make(chan os.Signal, 2048)
signal.Notify(signals)
// make sure runc is setup to use the monitor
// for waiting on processes
runc.Monitor = reaper.Default
return signals, nil
}
func newServer() (*ttrpc.Server, error) {
// for freebsd, we omit the socket credentials because these syscalls are
// slightly different. since we don't have freebsd support yet, this can be
// implemented later and the build can continue without issue.
return ttrpc.NewServer()
}

View File

@ -1,46 +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 main
import (
"os"
"os/signal"
"github.com/containerd/containerd/sys/reaper"
runc "github.com/containerd/go-runc"
"github.com/containerd/ttrpc"
"golang.org/x/sys/unix"
)
// setupSignals creates a new signal handler for all signals and sets the shim as a
// sub-reaper so that the container processes are reparented
func setupSignals() (chan os.Signal, error) {
signals := make(chan os.Signal, 32)
signal.Notify(signals, unix.SIGTERM, unix.SIGINT, unix.SIGCHLD, unix.SIGPIPE)
// make sure runc is setup to use the monitor
// for waiting on processes
runc.Monitor = reaper.Default
// set the shim as the subreaper for all orphaned processes created by the container
if err := reaper.SetSubreaper(1); err != nil {
return nil, err
}
return signals, nil
}
func newServer() (*ttrpc.Server, error) {
return ttrpc.NewServer(ttrpc.WithServerHandshaker(ttrpc.UnixSocketRequireSameUser()))
}

View File

@ -19,7 +19,6 @@ package builtins
import ( import (
_ "github.com/containerd/containerd/metrics/cgroups" _ "github.com/containerd/containerd/metrics/cgroups"
_ "github.com/containerd/containerd/metrics/cgroups/v2" _ "github.com/containerd/containerd/metrics/cgroups/v2"
_ "github.com/containerd/containerd/runtime/v1/linux"
_ "github.com/containerd/containerd/runtime/v2/runc/options" _ "github.com/containerd/containerd/runtime/v2/runc/options"
_ "github.com/containerd/containerd/snapshots/native/plugin" _ "github.com/containerd/containerd/snapshots/native/plugin"
_ "github.com/containerd/containerd/snapshots/overlay/plugin" _ "github.com/containerd/containerd/snapshots/overlay/plugin"

View File

@ -22,8 +22,6 @@ import (
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -86,38 +84,21 @@ func withCheckpointOpts(rt string, context *cli.Context) containerd.CheckpointTa
imagePath := context.String("image-path") imagePath := context.String("image-path")
workPath := context.String("work-path") workPath := context.String("work-path")
switch rt { if r.Options == nil {
case plugin.RuntimeRuncV1, plugin.RuntimeRuncV2: r.Options = &options.CheckpointOptions{}
if r.Options == nil {
r.Options = &options.CheckpointOptions{}
}
opts, _ := r.Options.(*options.CheckpointOptions)
if context.Bool("exit") {
opts.Exit = true
}
if imagePath != "" {
opts.ImagePath = imagePath
}
if workPath != "" {
opts.WorkPath = workPath
}
case plugin.RuntimeLinuxV1:
if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{}
}
opts, _ := r.Options.(*runctypes.CheckpointOptions)
if context.Bool("exit") {
opts.Exit = true
}
if imagePath != "" {
opts.ImagePath = imagePath
}
if workPath != "" {
opts.WorkPath = workPath
}
} }
opts, _ := r.Options.(*options.CheckpointOptions)
if context.Bool("exit") {
opts.Exit = true
}
if imagePath != "" {
opts.ImagePath = imagePath
}
if workPath != "" {
opts.WorkPath = workPath
}
return nil return nil
} }
} }

View File

@ -240,7 +240,7 @@ containerd_extra_runtime_handler=${CONTAINERD_EXTRA_RUNTIME_HANDLER:-""}
if [[ -n "${containerd_extra_runtime_handler}" ]]; then if [[ -n "${containerd_extra_runtime_handler}" ]]; then
cat >> ${config_path} <<EOF cat >> ${config_path} <<EOF
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${containerd_extra_runtime_handler}] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${containerd_extra_runtime_handler}]
runtime_type = "${CONTAINERD_EXTRA_RUNTIME_TYPE:-io.containerd.runc.v1}" runtime_type = "${CONTAINERD_EXTRA_RUNTIME_TYPE:-io.containerd.runc.v2}"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${containerd_extra_runtime_handler}.options] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${containerd_extra_runtime_handler}.options]
${CONTAINERD_EXTRA_RUNTIME_OPTIONS:-} ${CONTAINERD_EXTRA_RUNTIME_OPTIONS:-}

View File

@ -22,7 +22,7 @@ For each `containerd` release, we'll publish a release tarball specifically for
### Content ### Content
As shown below, the release tarball contains: As shown below, the release tarball contains:
- `containerd`, `containerd-shim`, `containerd-shim-runc-v1`, `containerd-shim-runc-v2`, `ctr`: binaries for containerd. - `containerd`, `containerd-shim-runc-v2`, `ctr`: binaries for containerd.
- `runc`: runc binary. - `runc`: runc binary.
- `/opt/cni/bin`: binaries for [Container Network Interface](https://github.com/containernetworking/cni) - `/opt/cni/bin`: binaries for [Container Network Interface](https://github.com/containernetworking/cni)
- `crictl`, `crictl.yaml`: command line tools for CRI container runtime and its config file. - `crictl`, `crictl.yaml`: command line tools for CRI container runtime and its config file.

View File

@ -18,8 +18,7 @@ package integration
import ( import (
// Register for linux platforms // Register for linux platforms
_ "github.com/containerd/containerd/plugins/sandbox" // WithInMemoryServices will fail otherwise _ "github.com/containerd/containerd/plugins/sandbox" // WithInMemoryServices will fail otherwise
_ "github.com/containerd/containerd/runtime/v1/linux"
_ "github.com/containerd/containerd/services/sandbox" // WithInMemoryServices will fail otherwise _ "github.com/containerd/containerd/services/sandbox" // WithInMemoryServices will fail otherwise
_ "github.com/containerd/containerd/snapshots/overlay/plugin" _ "github.com/containerd/containerd/snapshots/overlay/plugin"
) )

View File

@ -32,7 +32,6 @@ import (
. "github.com/containerd/containerd" . "github.com/containerd/containerd"
"github.com/containerd/containerd/cio" "github.com/containerd/containerd/cio"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/plugin"
) )
const ( const (
@ -48,9 +47,6 @@ func TestCheckpointRestorePTY(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer client.Close() defer client.Close()
if client.Runtime() == plugin.RuntimeLinuxV1 {
t.Skip()
}
var ( var (
ctx, cancel = testContext(t) ctx, cancel = testContext(t)
@ -174,9 +170,6 @@ func TestCheckpointRestore(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer client.Close() defer client.Close()
if client.Runtime() == plugin.RuntimeLinuxV1 {
t.Skip()
}
var ( var (
ctx, cancel = testContext(t) ctx, cancel = testContext(t)
@ -264,9 +257,6 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer client.Close() defer client.Close()
if client.Runtime() == plugin.RuntimeLinuxV1 {
t.Skip()
}
id := t.Name() id := t.Name()
ctx, cancel := testContext(t) ctx, cancel := testContext(t)
@ -354,9 +344,6 @@ func TestCheckpointLeaveRunning(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer client.Close() defer client.Close()
if client.Runtime() == plugin.RuntimeLinuxV1 {
t.Skip()
}
var ( var (
ctx, cancel = testContext(t) ctx, cancel = testContext(t)
@ -538,9 +525,6 @@ func TestCheckpointOnPauseStatus(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer client.Close() defer client.Close()
if client.Runtime() == plugin.RuntimeLinuxV1 {
t.Skip()
}
var ( var (
ctx, cancel = testContext(t) ctx, cancel = testContext(t)

View File

@ -39,7 +39,6 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/containerd/sys" "github.com/containerd/containerd/sys"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
@ -375,16 +374,9 @@ func TestDaemonReconnectsToShimIOPipesOnRestart(t *testing.T) {
// After we restarted containerd we write some messages to the log pipes, simulating shim writing stuff there. // After we restarted containerd we write some messages to the log pipes, simulating shim writing stuff there.
// Then we make sure that these messages are available on the containerd log thus proving that the server reconnected to the log pipes // Then we make sure that these messages are available on the containerd log thus proving that the server reconnected to the log pipes
runtimeVersion := getRuntimeVersion() logDirPath := getLogDirPath("v2", id)
logDirPath := getLogDirPath(runtimeVersion, id)
switch runtimeVersion { writeToFile(t, filepath.Join(logDirPath, "log"), fmt.Sprintf("%s writing to log\n", id))
case "v1":
writeToFile(t, filepath.Join(logDirPath, "shim.stdout.log"), fmt.Sprintf("%s writing to stdout\n", id))
writeToFile(t, filepath.Join(logDirPath, "shim.stderr.log"), fmt.Sprintf("%s writing to stderr\n", id))
case "v2":
writeToFile(t, filepath.Join(logDirPath, "log"), fmt.Sprintf("%s writing to log\n", id))
}
statusC, err := task.Wait(ctx) statusC, err := task.Wait(ctx)
if err != nil { if err != nil {
@ -402,18 +394,8 @@ func TestDaemonReconnectsToShimIOPipesOnRestart(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
switch runtimeVersion { if !strings.Contains(string(stdioContents), fmt.Sprintf("%s writing to log", id)) {
case "v1": t.Fatal("containerd did not connect to the shim log pipe")
if !strings.Contains(string(stdioContents), fmt.Sprintf("%s writing to stdout", id)) {
t.Fatal("containerd did not connect to the shim stdout pipe")
}
if !strings.Contains(string(stdioContents), fmt.Sprintf("%s writing to stderr", id)) {
t.Fatal("containerd did not connect to the shim stderr pipe")
}
case "v2":
if !strings.Contains(string(stdioContents), fmt.Sprintf("%s writing to log", id)) {
t.Fatal("containerd did not connect to the shim log pipe")
}
} }
} }
@ -432,8 +414,6 @@ func writeToFile(t *testing.T, filePath, message string) {
func getLogDirPath(runtimeVersion, id string) string { func getLogDirPath(runtimeVersion, id string) string {
switch runtimeVersion { switch runtimeVersion {
case "v1":
return filepath.Join(defaultRoot, plugin.RuntimeLinuxV1, testNamespace, id)
case "v2": case "v2":
return filepath.Join(defaultState, "io.containerd.runtime.v2.task", testNamespace, id) return filepath.Join(defaultState, "io.containerd.runtime.v2.task", testNamespace, id)
default: default:
@ -441,15 +421,6 @@ func getLogDirPath(runtimeVersion, id string) string {
} }
} }
func getRuntimeVersion() string {
switch rt := os.Getenv("TEST_RUNTIME"); rt {
case plugin.RuntimeLinuxV1:
return "v1"
default:
return "v2"
}
}
func TestContainerAttach(t *testing.T) { func TestContainerAttach(t *testing.T) {
t.Parallel() t.Parallel()
@ -1031,49 +1002,6 @@ func TestDaemonRestartWithRunningShim(t *testing.T) {
} }
} }
func TestContainerRuntimeOptionsv1(t *testing.T) {
t.Parallel()
client, err := newClient(t, address)
if err != nil {
t.Fatal(err)
}
defer client.Close()
var (
image Image
ctx, cancel = testContext(t)
id = t.Name()
)
defer cancel()
image, err = client.GetImage(ctx, testImage)
if err != nil {
t.Fatal(err)
}
container, err := client.NewContainer(
ctx, id,
WithNewSnapshot(id, image),
WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)),
WithRuntime(plugin.RuntimeLinuxV1, &runctypes.RuncOptions{Runtime: "no-runc"}),
)
if err != nil {
t.Fatal(err)
}
defer container.Delete(ctx, WithSnapshotCleanup)
task, err := container.NewTask(ctx, empty())
if err == nil {
t.Errorf("task creation should have failed")
task.Delete(ctx)
return
}
if !strings.Contains(err.Error(), `"no-runc"`) {
t.Errorf("task creation should have failed because of lack of executable. Instead failed with: %v", err.Error())
}
}
func TestContainerRuntimeOptionsv2(t *testing.T) { func TestContainerRuntimeOptionsv2(t *testing.T) {
t.Parallel() t.Parallel()
@ -1099,7 +1027,7 @@ func TestContainerRuntimeOptionsv2(t *testing.T) {
ctx, id, ctx, id,
WithNewSnapshot(id, image), WithNewSnapshot(id, image),
WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)), WithNewSpec(oci.WithImageConfig(image), withExitStatus(7)),
WithRuntime(plugin.RuntimeRuncV1, &options.Options{BinaryName: "no-runc"}), WithRuntime(plugin.RuntimeRuncV2, &options.Options{BinaryName: "no-runc"}),
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -1187,17 +1115,9 @@ func testUserNamespaces(t *testing.T, readonlyRootFS bool) {
} }
defer container.Delete(ctx, WithSnapshotCleanup) defer container.Delete(ctx, WithSnapshotCleanup)
var copts interface{} copts := &options.Options{
if CheckRuntime(client.Runtime(), "io.containerd.runc") { IoUid: 1000,
copts = &options.Options{ IoGid: 2000,
IoUid: 1000,
IoGid: 2000,
}
} else {
copts = &runctypes.CreateOptions{
IoUid: 1000,
IoGid: 2000,
}
} }
task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio), func(_ context.Context, client *Client, r *TaskInfo) error { task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio), func(_ context.Context, client *Client, r *TaskInfo) error {

View File

@ -39,7 +39,6 @@ import (
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
gogotypes "github.com/containerd/containerd/protobuf/types" gogotypes "github.com/containerd/containerd/protobuf/types"
_ "github.com/containerd/containerd/runtime" _ "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
@ -670,10 +669,6 @@ func TestKillContainerDeletedByRunc(t *testing.T) {
} }
defer client.Close() defer client.Close()
if client.Runtime() == plugin.RuntimeLinuxV1 {
t.Skip("test relies on runtime v2")
}
var ( var (
image Image image Image
ctx, cancel = testContext(t) ctx, cancel = testContext(t)

View File

@ -55,7 +55,7 @@ version = 2
} }
id := t.Name() id := t.Name()
container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("top")), WithRuntime(plugin.RuntimeRuncV1, &options.Options{ container, err := client.NewContainer(ctx, id, WithNewSnapshot(id, image), WithNewSpec(oci.WithImageConfig(image), withProcessArgs("top")), WithRuntime(plugin.RuntimeRuncV2, &options.Options{
Root: runtimeRoot, Root: runtimeRoot,
})) }))
if err != nil { if err != nil {

View File

@ -1,64 +0,0 @@
//go: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 client
import (
"context"
"testing"
. "github.com/containerd/containerd"
"github.com/containerd/containerd/runtime/linux/runctypes"
)
func TestWithNoNewKeyringAddsNoNewKeyringToOptions(t *testing.T) {
var taskInfo TaskInfo
var ctx context.Context
var client Client
err := WithNoNewKeyring(ctx, &client, &taskInfo)
if err != nil {
t.Fatal(err)
}
opts := taskInfo.Options.(*runctypes.CreateOptions)
if !opts.NoNewKeyring {
t.Fatal("NoNewKeyring set on WithNoNewKeyring")
}
}
func TestWithNoNewKeyringDoesNotOverwriteOtherOptions(t *testing.T) {
var taskInfo TaskInfo
var ctx context.Context
var client Client
taskInfo.Options = &runctypes.CreateOptions{NoPivotRoot: true}
err := WithNoNewKeyring(ctx, &client, &taskInfo)
if err != nil {
t.Fatal(err)
}
opts := taskInfo.Options.(*runctypes.CreateOptions)
if !opts.NoPivotRoot {
t.Fatal("WithNoNewKeyring overwrote other options")
}
}

View File

@ -30,7 +30,6 @@ import (
"testing" "testing"
"time" "time"
v1shimcli "github.com/containerd/containerd/runtime/v1/shim/client"
v2shimcli "github.com/containerd/containerd/runtime/v2/shim" v2shimcli "github.com/containerd/containerd/runtime/v2/shim"
"github.com/containerd/ttrpc" "github.com/containerd/ttrpc"
) )
@ -47,10 +46,8 @@ func TestFailFastWhenConnectShim(t *testing.T) {
// abstract Unix domain sockets are only for Linux. // abstract Unix domain sockets are only for Linux.
if runtime.GOOS == "linux" { if runtime.GOOS == "linux" {
t.Run("abstract-unix-socket-v1", testFailFastWhenConnectShim(true, v1shimcli.AnonDialer))
t.Run("abstract-unix-socket-v2", testFailFastWhenConnectShim(true, v2shimcli.AnonDialer)) t.Run("abstract-unix-socket-v2", testFailFastWhenConnectShim(true, v2shimcli.AnonDialer))
} }
t.Run("normal-unix-socket-v1", testFailFastWhenConnectShim(false, v1shimcli.AnonDialer))
t.Run("normal-unix-socket-v2", testFailFastWhenConnectShim(false, v2shimcli.AnonDialer)) t.Run("normal-unix-socket-v2", testFailFastWhenConnectShim(false, v2shimcli.AnonDialer))
} }

View File

@ -28,7 +28,6 @@ import (
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/v1/linux"
"github.com/docker/go-metrics" "github.com/docker/go-metrics"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -55,11 +54,15 @@ type cgroupsMonitor struct {
publisher events.Publisher publisher events.Publisher
} }
type cgroupTask interface {
Cgroup() (cgroups.Cgroup, error)
}
func (m *cgroupsMonitor) Monitor(c runtime.Task, labels map[string]string) error { func (m *cgroupsMonitor) Monitor(c runtime.Task, labels map[string]string) error {
if err := m.collector.Add(c, labels); err != nil { if err := m.collector.Add(c, labels); err != nil {
return err return err
} }
t, ok := c.(*linux.Task) t, ok := c.(cgroupTask)
if !ok { if !ok {
return nil return nil
} }

View File

@ -24,7 +24,6 @@ import (
"time" "time"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin"
) )
type SandboxControllerMode string type SandboxControllerMode string
@ -46,10 +45,6 @@ type Runtime struct {
// When specified, containerd will ignore runtime name field when resolving shim location. // When specified, containerd will ignore runtime name field when resolving shim location.
// Path must be abs. // Path must be abs.
Path string `toml:"runtime_path" json:"runtimePath"` Path string `toml:"runtime_path" json:"runtimePath"`
// Engine is the name of the runtime engine used by containerd.
// This only works for runtime type "io.containerd.runtime.v1.linux".
// DEPRECATED: use Options instead. Remove when shim v1 is deprecated.
Engine string `toml:"runtime_engine" json:"runtimeEngine"`
// PodAnnotations is a list of pod annotations passed to both pod sandbox as well as // PodAnnotations is a list of pod annotations passed to both pod sandbox as well as
// container OCI annotations. // container OCI annotations.
PodAnnotations []string `toml:"pod_annotations" json:"PodAnnotations"` PodAnnotations []string `toml:"pod_annotations" json:"PodAnnotations"`
@ -57,10 +52,6 @@ type Runtime struct {
// Container annotations in CRI are usually generated by other Kubernetes node components (i.e., not users). // Container annotations in CRI are usually generated by other Kubernetes node components (i.e., not users).
// Currently, only device plugins populate the annotations. // Currently, only device plugins populate the annotations.
ContainerAnnotations []string `toml:"container_annotations" json:"ContainerAnnotations"` ContainerAnnotations []string `toml:"container_annotations" json:"ContainerAnnotations"`
// Root is the directory used by containerd for runtime state.
// DEPRECATED: use Options instead. Remove when shim v1 is deprecated.
// This only works for runtime type "io.containerd.runtime.v1.linux".
Root string `toml:"runtime_root" json:"runtimeRoot"`
// Options are config options for the runtime. // Options are config options for the runtime.
// If options is loaded from toml config, it will be map[string]interface{}. // If options is loaded from toml config, it will be map[string]interface{}.
// Options can be converted into toml.Tree using toml.TreeFromMap(). // Options can be converted into toml.Tree using toml.TreeFromMap().
@ -99,19 +90,10 @@ type ContainerdConfig struct {
Snapshotter string `toml:"snapshotter" json:"snapshotter"` Snapshotter string `toml:"snapshotter" json:"snapshotter"`
// DefaultRuntimeName is the default runtime name to use from the runtimes table. // DefaultRuntimeName is the default runtime name to use from the runtimes table.
DefaultRuntimeName string `toml:"default_runtime_name" json:"defaultRuntimeName"` DefaultRuntimeName string `toml:"default_runtime_name" json:"defaultRuntimeName"`
// DefaultRuntime is the default runtime to use in containerd.
// This runtime is used when no runtime handler (or the empty string) is provided.
// DEPRECATED: use DefaultRuntimeName instead. Remove in containerd 1.4.
DefaultRuntime Runtime `toml:"default_runtime" json:"defaultRuntime"`
// UntrustedWorkloadRuntime is a runtime to run untrusted workloads on it.
// DEPRECATED: use `untrusted` runtime in Runtimes instead. Remove in containerd 1.4.
UntrustedWorkloadRuntime Runtime `toml:"untrusted_workload_runtime" json:"untrustedWorkloadRuntime"`
// Runtimes is a map from CRI RuntimeHandler strings, which specify types of runtime // Runtimes is a map from CRI RuntimeHandler strings, which specify types of runtime
// configurations, to the matching configurations. // configurations, to the matching configurations.
Runtimes map[string]Runtime `toml:"runtimes" json:"runtimes"` Runtimes map[string]Runtime `toml:"runtimes" json:"runtimes"`
// NoPivot disables pivot-root (linux only), required when running a container in a RamDisk with runc
// This only works for runtime type "io.containerd.runtime.v1.linux".
NoPivot bool `toml:"no_pivot" json:"noPivot"`
// DisableSnapshotAnnotations disables to pass additional annotations (image // DisableSnapshotAnnotations disables to pass additional annotations (image
// related information) to snapshotters. These annotations are required by // related information) to snapshotters. These annotations are required by
@ -274,10 +256,6 @@ type PluginConfig struct {
SandboxImage string `toml:"sandbox_image" json:"sandboxImage"` SandboxImage string `toml:"sandbox_image" json:"sandboxImage"`
// StatsCollectPeriod is the period (in seconds) of snapshots stats collection. // StatsCollectPeriod is the period (in seconds) of snapshots stats collection.
StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod"` StatsCollectPeriod int `toml:"stats_collect_period" json:"statsCollectPeriod"`
// SystemdCgroup enables systemd cgroup support.
// This only works for runtime type "io.containerd.runtime.v1.linux".
// DEPRECATED: config runc runtime handler instead. Remove when shim v1 is deprecated.
SystemdCgroup bool `toml:"systemd_cgroup" json:"systemdCgroup"`
// EnableTLSStreaming indicates to enable the TLS streaming support. // EnableTLSStreaming indicates to enable the TLS streaming support.
EnableTLSStreaming bool `toml:"enable_tls_streaming" json:"enableTLSStreaming"` EnableTLSStreaming bool `toml:"enable_tls_streaming" json:"enableTLSStreaming"`
// X509KeyPairStreaming is a x509 key pair used for TLS streaming // X509KeyPairStreaming is a x509 key pair used for TLS streaming
@ -401,22 +379,6 @@ func ValidatePluginConfig(ctx context.Context, c *PluginConfig) error {
c.ContainerdConfig.Runtimes = make(map[string]Runtime) c.ContainerdConfig.Runtimes = make(map[string]Runtime)
} }
// Validation for deprecated untrusted_workload_runtime.
if c.ContainerdConfig.UntrustedWorkloadRuntime.Type != "" {
log.G(ctx).Warning("`untrusted_workload_runtime` is deprecated, please use `untrusted` runtime in `runtimes` instead")
if _, ok := c.ContainerdConfig.Runtimes[RuntimeUntrusted]; ok {
return fmt.Errorf("conflicting definitions: configuration includes both `untrusted_workload_runtime` and `runtimes[%q]`", RuntimeUntrusted)
}
c.ContainerdConfig.Runtimes[RuntimeUntrusted] = c.ContainerdConfig.UntrustedWorkloadRuntime
}
// Validation for deprecated default_runtime field.
if c.ContainerdConfig.DefaultRuntime.Type != "" {
log.G(ctx).Warning("`default_runtime` is deprecated, please use `default_runtime_name` to reference the default configuration you have defined in `runtimes`")
c.ContainerdConfig.DefaultRuntimeName = RuntimeDefault
c.ContainerdConfig.Runtimes[RuntimeDefault] = c.ContainerdConfig.DefaultRuntime
}
// Validation for default_runtime_name // Validation for default_runtime_name
if c.ContainerdConfig.DefaultRuntimeName == "" { if c.ContainerdConfig.DefaultRuntimeName == "" {
return errors.New("`default_runtime_name` is empty") return errors.New("`default_runtime_name` is empty")
@ -425,33 +387,7 @@ func ValidatePluginConfig(ctx context.Context, c *PluginConfig) error {
return fmt.Errorf("no corresponding runtime configured in `containerd.runtimes` for `containerd` `default_runtime_name = \"%s\"", c.ContainerdConfig.DefaultRuntimeName) return fmt.Errorf("no corresponding runtime configured in `containerd.runtimes` for `containerd` `default_runtime_name = \"%s\"", c.ContainerdConfig.DefaultRuntimeName)
} }
// Validation for deprecated runtime options.
if c.SystemdCgroup {
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
return fmt.Errorf("`systemd_cgroup` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`systemd_cgroup` is deprecated, please use runtime `options` instead")
}
if c.NoPivot {
if c.ContainerdConfig.Runtimes[c.ContainerdConfig.DefaultRuntimeName].Type != plugin.RuntimeLinuxV1 {
return fmt.Errorf("`no_pivot` only works for runtime %s", plugin.RuntimeLinuxV1)
}
// NoPivot can't be deprecated yet, because there is no alternative config option
// for `io.containerd.runtime.v1.linux`.
}
for k, r := range c.ContainerdConfig.Runtimes { for k, r := range c.ContainerdConfig.Runtimes {
if r.Engine != "" {
if r.Type != plugin.RuntimeLinuxV1 {
return fmt.Errorf("`runtime_engine` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`runtime_engine` is deprecated, please use runtime `options` instead")
}
if r.Root != "" {
if r.Type != plugin.RuntimeLinuxV1 {
return fmt.Errorf("`runtime_root` only works for runtime %s", plugin.RuntimeLinuxV1)
}
log.G(ctx).Warning("`runtime_root` is deprecated, please use runtime `options` instead")
}
if !r.PrivilegedWithoutHostDevices && r.PrivilegedWithoutHostDevicesAllDevicesAllowed { if !r.PrivilegedWithoutHostDevices && r.PrivilegedWithoutHostDevicesAllDevicesAllowed {
return errors.New("`privileged_without_host_devices_all_devices_allowed` requires `privileged_without_host_devices` to be enabled") return errors.New("`privileged_without_host_devices_all_devices_allowed` requires `privileged_without_host_devices` to be enabled")
} }

View File

@ -18,10 +18,8 @@ package config
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"github.com/containerd/containerd/plugin"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -31,81 +29,6 @@ func TestValidateConfig(t *testing.T) {
expectedErr string expectedErr string
expected *PluginConfig expected *PluginConfig
}{ }{
"deprecated untrusted_workload_runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
UntrustedWorkloadRuntime: Runtime{
Type: "untrusted",
},
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: "default",
},
},
},
},
expected: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
UntrustedWorkloadRuntime: Runtime{
Type: "untrusted",
},
Runtimes: map[string]Runtime{
RuntimeUntrusted: {
Type: "untrusted",
SandboxMode: string(ModePodSandbox),
},
RuntimeDefault: {
Type: "default",
SandboxMode: string(ModePodSandbox),
},
},
},
},
},
"both untrusted_workload_runtime and runtime[untrusted]": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
UntrustedWorkloadRuntime: Runtime{
Type: "untrusted-1",
},
Runtimes: map[string]Runtime{
RuntimeUntrusted: {
Type: "untrusted-2",
},
RuntimeDefault: {
Type: "default",
},
},
},
},
expectedErr: fmt.Sprintf("conflicting definitions: configuration includes both `untrusted_workload_runtime` and `runtimes[%q]`", RuntimeUntrusted),
},
"deprecated default_runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntime: Runtime{
Type: "default",
},
},
},
expected: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntime: Runtime{
Type: "default",
},
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: "default",
SandboxMode: string(ModePodSandbox),
},
},
},
},
},
"no default_runtime_name": { "no default_runtime_name": {
config: &PluginConfig{}, config: &PluginConfig{},
expectedErr: "`default_runtime_name` is empty", expectedErr: "`default_runtime_name` is empty",
@ -118,170 +41,13 @@ func TestValidateConfig(t *testing.T) {
}, },
expectedErr: "no corresponding runtime configured in `containerd.runtimes` for `containerd` `default_runtime_name = \"default\"", expectedErr: "no corresponding runtime configured in `containerd.runtimes` for `containerd` `default_runtime_name = \"default\"",
}, },
"deprecated systemd_cgroup for v1 runtime": {
config: &PluginConfig{
SystemdCgroup: true,
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: plugin.RuntimeLinuxV1,
},
},
},
},
expected: &PluginConfig{
SystemdCgroup: true,
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: plugin.RuntimeLinuxV1,
SandboxMode: string(ModePodSandbox),
},
},
},
},
},
"deprecated systemd_cgroup for v2 runtime": {
config: &PluginConfig{
SystemdCgroup: true,
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: plugin.RuntimeRuncV1,
},
},
},
},
expectedErr: fmt.Sprintf("`systemd_cgroup` only works for runtime %s", plugin.RuntimeLinuxV1),
},
"no_pivot for v1 runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
NoPivot: true,
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: plugin.RuntimeLinuxV1,
},
},
},
},
expected: &PluginConfig{
ContainerdConfig: ContainerdConfig{
NoPivot: true,
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: plugin.RuntimeLinuxV1,
SandboxMode: string(ModePodSandbox),
},
},
},
},
},
"no_pivot for v2 runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
NoPivot: true,
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Type: plugin.RuntimeRuncV1,
},
},
},
},
expectedErr: fmt.Sprintf("`no_pivot` only works for runtime %s", plugin.RuntimeLinuxV1),
},
"deprecated runtime_engine for v1 runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Engine: "runc",
Type: plugin.RuntimeLinuxV1,
},
},
},
},
expected: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Engine: "runc",
Type: plugin.RuntimeLinuxV1,
SandboxMode: string(ModePodSandbox),
},
},
},
},
},
"deprecated runtime_engine for v2 runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Engine: "runc",
Type: plugin.RuntimeRuncV1,
},
},
},
},
expectedErr: fmt.Sprintf("`runtime_engine` only works for runtime %s", plugin.RuntimeLinuxV1),
},
"deprecated runtime_root for v1 runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Root: "/run/containerd/runc",
Type: plugin.RuntimeLinuxV1,
},
},
},
},
expected: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Root: "/run/containerd/runc",
Type: plugin.RuntimeLinuxV1,
SandboxMode: string(ModePodSandbox),
},
},
},
},
},
"deprecated runtime_root for v2 runtime": {
config: &PluginConfig{
ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{
RuntimeDefault: {
Root: "/run/containerd/runc",
Type: plugin.RuntimeRuncV1,
},
},
},
},
expectedErr: fmt.Sprintf("`runtime_root` only works for runtime %s", plugin.RuntimeLinuxV1),
},
"deprecated auths": { "deprecated auths": {
config: &PluginConfig{ config: &PluginConfig{
ContainerdConfig: ContainerdConfig{ ContainerdConfig: ContainerdConfig{
DefaultRuntimeName: RuntimeDefault, DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{ Runtimes: map[string]Runtime{
RuntimeDefault: { RuntimeDefault: {},
Type: plugin.RuntimeRuncV1,
},
}, },
}, },
Registry: Registry{ Registry: Registry{
@ -295,7 +61,6 @@ func TestValidateConfig(t *testing.T) {
DefaultRuntimeName: RuntimeDefault, DefaultRuntimeName: RuntimeDefault,
Runtimes: map[string]Runtime{ Runtimes: map[string]Runtime{
RuntimeDefault: { RuntimeDefault: {
Type: plugin.RuntimeRuncV1,
SandboxMode: string(ModePodSandbox), SandboxMode: string(ModePodSandbox),
}, },
}, },

View File

@ -29,9 +29,6 @@ import (
// DefaultConfig returns default configurations of cri plugin. // DefaultConfig returns default configurations of cri plugin.
func DefaultConfig() PluginConfig { func DefaultConfig() PluginConfig {
defaultRuncV2Opts := ` defaultRuncV2Opts := `
# NoPivotRoot disables pivot root when creating a container.
NoPivotRoot = false
# NoNewKeyring disables new keyring for the container. # NoNewKeyring disables new keyring for the container.
NoNewKeyring = false NoNewKeyring = false
@ -53,9 +50,6 @@ func DefaultConfig() PluginConfig {
# CriuPath is the criu binary path. # CriuPath is the criu binary path.
CriuPath = "" CriuPath = ""
# SystemdCgroup enables systemd cgroups.
SystemdCgroup = false
# CriuImagePath is the criu image path # CriuImagePath is the criu image path
CriuImagePath = "" CriuImagePath = ""
@ -74,7 +68,6 @@ func DefaultConfig() PluginConfig {
ContainerdConfig: ContainerdConfig{ ContainerdConfig: ContainerdConfig{
Snapshotter: containerd.DefaultSnapshotter, Snapshotter: containerd.DefaultSnapshotter,
DefaultRuntimeName: "runc", DefaultRuntimeName: "runc",
NoPivot: false,
Runtimes: map[string]Runtime{ Runtimes: map[string]Runtime{
"runc": { "runc": {
Type: "io.containerd.runc.v2", Type: "io.containerd.runc.v2",
@ -97,7 +90,6 @@ func DefaultConfig() PluginConfig {
}, },
SandboxImage: "registry.k8s.io/pause:3.8", SandboxImage: "registry.k8s.io/pause:3.8",
StatsCollectPeriod: 10, StatsCollectPeriod: 10,
SystemdCgroup: false,
MaxContainerLogLineSize: 16 * 1024, MaxContainerLogLineSize: 16 * 1024,
MaxConcurrentDownloads: 3, MaxConcurrentDownloads: 3,
DisableProcMount: false, DisableProcMount: false,

View File

@ -38,7 +38,6 @@ func DefaultConfig() PluginConfig {
ContainerdConfig: ContainerdConfig{ ContainerdConfig: ContainerdConfig{
Snapshotter: containerd.DefaultSnapshotter, Snapshotter: containerd.DefaultSnapshotter,
DefaultRuntimeName: "runhcs-wcow-process", DefaultRuntimeName: "runhcs-wcow-process",
NoPivot: false,
Runtimes: map[string]Runtime{ Runtimes: map[string]Runtime{
"runhcs-wcow-process": { "runhcs-wcow-process": {
Type: "io.containerd.runhcs.v1", Type: "io.containerd.runhcs.v1",

View File

@ -1,38 +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 opts
import (
"context"
"github.com/containerd/containerd"
"github.com/containerd/containerd/runtime/linux/runctypes"
)
// WithContainerdShimCgroup returns function that sets the containerd
// shim cgroup path
func WithContainerdShimCgroup(path string) containerd.NewTaskOpts {
return func(_ context.Context, _ *containerd.Client, r *containerd.TaskInfo) error {
r.Options = &runctypes.CreateOptions{
ShimCgroup: path,
}
return nil
}
}
//TODO: Since Options is an interface different WithXXX will be needed to set different
// combinations of CreateOptions.

View File

@ -110,17 +110,12 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
return cntr.IO, nil return cntr.IO, nil
} }
ctrInfo, err := container.Info(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get container info: %w", err)
}
ociRuntime, err := c.getSandboxRuntime(sandbox.Config, sandbox.Metadata.RuntimeHandler) ociRuntime, err := c.getSandboxRuntime(sandbox.Config, sandbox.Metadata.RuntimeHandler)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get sandbox runtime: %w", err) return nil, fmt.Errorf("failed to get sandbox runtime: %w", err)
} }
taskOpts := c.taskOpts(ctrInfo.Runtime.Name) var taskOpts []containerd.NewTaskOpts
if ociRuntime.Path != "" { if ociRuntime.Path != "" {
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path)) taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
} }

View File

@ -42,7 +42,6 @@ import (
runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1" runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/reference/docker" "github.com/containerd/containerd/reference/docker"
"github.com/containerd/containerd/runtime/linux/runctypes"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options" runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
@ -330,15 +329,7 @@ func parseImageReferences(refs []string) ([]string, []string) {
// generateRuntimeOptions generates runtime options from cri plugin config. // generateRuntimeOptions generates runtime options from cri plugin config.
func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) { func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) {
if r.Options == nil { if r.Options == nil {
if r.Type != plugin.RuntimeLinuxV1 { return nil, nil
return nil, nil
}
// This is a legacy config, generate runctypes.RuncOptions.
return &runctypes.RuncOptions{
Runtime: r.Engine,
RuntimeRoot: r.Root,
SystemdCgroup: c.SystemdCgroup,
}, nil
} }
optionsTree, err := toml.TreeFromMap(r.Options) optionsTree, err := toml.TreeFromMap(r.Options)
if err != nil { if err != nil {
@ -364,12 +355,8 @@ func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{
// getRuntimeOptionsType gets empty runtime options by the runtime type name. // getRuntimeOptionsType gets empty runtime options by the runtime type name.
func getRuntimeOptionsType(t string) interface{} { func getRuntimeOptionsType(t string) interface{} {
switch t { switch t {
case plugin.RuntimeRuncV1:
fallthrough
case plugin.RuntimeRuncV2: case plugin.RuntimeRuncV2:
return &runcoptions.Options{} return &runcoptions.Options{}
case plugin.RuntimeLinuxV1:
return &runctypes.RuncOptions{}
case runtimeRunhcsV1: case runtimeRunhcsV1:
return &runhcsoptions.Options{} return &runhcsoptions.Options{}
default: default:

View File

@ -35,7 +35,6 @@ import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/protobuf/types" "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/reference/docker" "github.com/containerd/containerd/reference/docker"
"github.com/containerd/containerd/runtime/linux/runctypes"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
@ -210,10 +209,6 @@ systemd_cgroup = true
[containerd] [containerd]
no_pivot = true no_pivot = true
default_runtime_name = "default" default_runtime_name = "default"
[containerd.runtimes.legacy]
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
[containerd.runtimes.runc]
runtime_type = "` + plugin.RuntimeRuncV1 + `"
[containerd.runtimes.runcv2] [containerd.runtimes.runcv2]
runtime_type = "` + plugin.RuntimeRuncV2 + `" runtime_type = "` + plugin.RuntimeRuncV2 + `"
` `
@ -222,13 +217,9 @@ systemd_cgroup = true
[containerd] [containerd]
no_pivot = true no_pivot = true
default_runtime_name = "default" default_runtime_name = "default"
[containerd.runtimes.legacy]
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
[containerd.runtimes.legacy.options] [containerd.runtimes.legacy.options]
Runtime = "legacy" Runtime = "legacy"
RuntimeRoot = "/legacy" RuntimeRoot = "/legacy"
[containerd.runtimes.runc]
runtime_type = "` + plugin.RuntimeRuncV1 + `"
[containerd.runtimes.runc.options] [containerd.runtimes.runc.options]
BinaryName = "runc" BinaryName = "runc"
Root = "/runc" Root = "/runc"
@ -245,7 +236,7 @@ systemd_cgroup = true
require.NoError(t, err) require.NoError(t, err)
err = tree.Unmarshal(&nilOptsConfig) err = tree.Unmarshal(&nilOptsConfig)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, nilOptsConfig.Runtimes, 3) require.Len(t, nilOptsConfig.Runtimes, 1)
tree, err = toml.Load(nonNilOpts) tree, err = toml.Load(nonNilOpts)
require.NoError(t, err) require.NoError(t, err)
@ -258,32 +249,11 @@ systemd_cgroup = true
c criconfig.Config c criconfig.Config
expectedOptions interface{} expectedOptions interface{}
}{ }{
"when options is nil, should return nil option for io.containerd.runc.v1": {
r: nilOptsConfig.Runtimes["runc"],
c: nilOptsConfig,
expectedOptions: nil,
},
"when options is nil, should return nil option for io.containerd.runc.v2": { "when options is nil, should return nil option for io.containerd.runc.v2": {
r: nilOptsConfig.Runtimes["runcv2"], r: nilOptsConfig.Runtimes["runcv2"],
c: nilOptsConfig, c: nilOptsConfig,
expectedOptions: nil, expectedOptions: nil,
}, },
"when options is nil, should use legacy fields for legacy runtime": {
r: nilOptsConfig.Runtimes["legacy"],
c: nilOptsConfig,
expectedOptions: &runctypes.RuncOptions{
SystemdCgroup: true,
},
},
"when options is not nil, should be able to decode for io.containerd.runc.v1": {
r: nonNilOptsConfig.Runtimes["runc"],
c: nonNilOptsConfig,
expectedOptions: &runcoptions.Options{
BinaryName: "runc",
Root: "/runc",
NoNewKeyring: true,
},
},
"when options is not nil, should be able to decode for io.containerd.runc.v2": { "when options is not nil, should be able to decode for io.containerd.runc.v2": {
r: nonNilOptsConfig.Runtimes["runcv2"], r: nonNilOptsConfig.Runtimes["runcv2"],
c: nonNilOptsConfig, c: nonNilOptsConfig,
@ -293,14 +263,6 @@ systemd_cgroup = true
NoNewKeyring: true, NoNewKeyring: true,
}, },
}, },
"when options is not nil, should be able to decode for legacy runtime": {
r: nonNilOptsConfig.Runtimes["legacy"],
c: nonNilOptsConfig,
expectedOptions: &runctypes.RuncOptions{
Runtime: "legacy",
RuntimeRoot: "/legacy",
},
},
} { } {
t.Run(desc, func(t *testing.T) { t.Run(desc, func(t *testing.T) {
opts, err := generateRuntimeOptions(test.r, test.c) opts, err := generateRuntimeOptions(test.r, test.c)

View File

@ -212,7 +212,7 @@ func (c *Controller) Start(ctx context.Context, id string) (cin sandbox.Controll
// Create sandbox task in containerd. // Create sandbox task in containerd.
log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).", id, metadata.Name) log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).", id, metadata.Name)
taskOpts := c.taskOpts(ociRuntime.Type) var taskOpts []containerd.NewTaskOpts
if ociRuntime.Path != "" { if ociRuntime.Path != "" {
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path)) taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
} }

View File

@ -22,9 +22,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/plugin"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go" runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux"
@ -325,19 +323,3 @@ func (c *Controller) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
} }
return nil return nil
} }
// taskOpts generates task options for a (sandbox) container.
func (c *Controller) taskOpts(runtimeType string) []containerd.NewTaskOpts {
// TODO(random-liu): Remove this after shim v1 is deprecated.
var taskOpts []containerd.NewTaskOpts
// c.config.NoPivot is only supported for RuntimeLinuxV1 = "io.containerd.runtime.v1.linux" legacy linux runtime
// and is not supported for RuntimeRuncV1 = "io.containerd.runc.v1" or RuntimeRuncV2 = "io.containerd.runc.v2"
// for RuncV1/2 no pivot is set under the containerd.runtimes.runc.options config see
// https://github.com/containerd/containerd/blob/v1.3.2/runtime/v2/runc/options/oci.pb.go#L26
if c.config.NoPivot && runtimeType == plugin.RuntimeLinuxV1 {
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
}
return taskOpts
}

View File

@ -19,7 +19,6 @@
package podsandbox package podsandbox
import ( import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/pkg/cri/annotations" "github.com/containerd/containerd/pkg/cri/annotations"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
@ -49,8 +48,3 @@ func (c *Controller) setupSandboxFiles(id string, config *runtime.PodSandboxConf
func (c *Controller) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error { func (c *Controller) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
return nil return nil
} }
// taskOpts generates task options for a (sandbox) container.
func (c *Controller) taskOpts(runtimeType string) []containerd.NewTaskOpts {
return []containerd.NewTaskOpts{}
}

View File

@ -26,8 +26,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1" runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"github.com/containerd/containerd/pkg/cri/annotations"
criconfig "github.com/containerd/containerd/pkg/cri/config"
sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox"
) )
@ -207,166 +205,3 @@ func TestHostAccessingSandbox(t *testing.T) {
}) })
} }
} }
func TestGetSandboxRuntime(t *testing.T) {
untrustedWorkloadRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "untrusted-workload-runtime",
Root: "",
}
defaultRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "default-runtime",
Root: "",
}
fooRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "foo-bar",
Root: "",
}
for desc, test := range map[string]struct {
sandboxConfig *runtime.PodSandboxConfig
runtimeHandler string
runtimes map[string]criconfig.Runtime
expectErr bool
expectedRuntime criconfig.Runtime
}{
"should return error if untrusted workload requires host access": {
sandboxConfig: &runtime.PodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{
SecurityContext: &runtime.LinuxSandboxSecurityContext{
Privileged: false,
NamespaceOptions: &runtime.NamespaceOption{
Network: runtime.NamespaceMode_NODE,
Pid: runtime.NamespaceMode_NODE,
Ipc: runtime.NamespaceMode_NODE,
},
},
},
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectErr: true,
},
"should use untrusted workload runtime for untrusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: untrustedWorkloadRuntime,
},
"should use default runtime for regular workload": {
sandboxConfig: &runtime.PodSandboxConfig{},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
},
expectedRuntime: defaultRuntime,
},
"should use default runtime for trusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "false",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: defaultRuntime,
},
"should return error if untrusted workload runtime is required but not configured": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
},
expectErr: true,
},
"should use 'untrusted' runtime for untrusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: untrustedWorkloadRuntime,
},
"should use 'untrusted' runtime for untrusted workload & handler": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimeHandler: "untrusted",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: untrustedWorkloadRuntime,
},
"should return an error if untrusted annotation with conflicting handler": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimeHandler: "foo",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
"foo": fooRuntime,
},
expectErr: true,
},
"should use correct runtime for a runtime handler": {
sandboxConfig: &runtime.PodSandboxConfig{},
runtimeHandler: "foo",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
"foo": fooRuntime,
},
expectedRuntime: fooRuntime,
},
"should return error if runtime handler is required but not configured": {
sandboxConfig: &runtime.PodSandboxConfig{},
runtimeHandler: "bar",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
"foo": fooRuntime,
},
expectErr: true,
},
} {
t.Run(desc, func(t *testing.T) {
cri := newControllerService()
cri.config = criconfig.Config{
PluginConfig: criconfig.DefaultConfig(),
}
cri.config.ContainerdConfig.DefaultRuntimeName = criconfig.RuntimeDefault
cri.config.ContainerdConfig.Runtimes = test.runtimes
r, err := cri.getSandboxRuntime(test.sandboxConfig, test.runtimeHandler)
assert.Equal(t, test.expectErr, err != nil)
assert.Equal(t, test.expectedRuntime, r)
})
}
}

View File

@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go" runtimespec "github.com/opencontainers/runtime-spec/specs-go"
@ -102,8 +101,3 @@ func (c *Controller) setupSandboxFiles(id string, config *runtime.PodSandboxConf
func (c *Controller) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error { func (c *Controller) cleanupSandboxFiles(id string, config *runtime.PodSandboxConfig) error {
return nil return nil
} }
// No task options needed for windows.
func (c *Controller) taskOpts(runtimeType string) []containerd.NewTaskOpts {
return nil
}

View File

@ -1,38 +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 sbserver
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/plugin"
)
// taskOpts generates task options for a (sandbox) container.
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
// TODO(random-liu): Remove this after shim v1 is deprecated.
var taskOpts []containerd.NewTaskOpts
// c.config.NoPivot is only supported for RuntimeLinuxV1 = "io.containerd.runtime.v1.linux" legacy linux runtime
// and is not supported for RuntimeRuncV1 = "io.containerd.runc.v1" or RuntimeRuncV2 = "io.containerd.runc.v2"
// for RuncV1/2 no pivot is set under the containerd.runtimes.runc.options config see
// https://github.com/containerd/containerd/blob/v1.3.2/runtime/v2/runc/options/oci.pb.go#L26
if c.config.NoPivot && runtimeType == plugin.RuntimeLinuxV1 {
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
}
return taskOpts
}

View File

@ -1,28 +0,0 @@
//go:build !windows && !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 sbserver
import (
"github.com/containerd/containerd"
)
// taskOpts generates task options for a (sandbox) container.
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
return []containerd.NewTaskOpts{}
}

View File

@ -1,26 +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 sbserver
import (
"github.com/containerd/containerd"
)
// No task options needed for windows.
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
return nil
}

View File

@ -110,17 +110,12 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
return cntr.IO, nil return cntr.IO, nil
} }
ctrInfo, err := container.Info(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get container info: %w", err)
}
ociRuntime, err := c.getSandboxRuntime(sandbox.Config, sandbox.Metadata.RuntimeHandler) ociRuntime, err := c.getSandboxRuntime(sandbox.Config, sandbox.Metadata.RuntimeHandler)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get sandbox runtime: %w", err) return nil, fmt.Errorf("failed to get sandbox runtime: %w", err)
} }
taskOpts := c.taskOpts(ctrInfo.Runtime.Name) var taskOpts []containerd.NewTaskOpts
if ociRuntime.Path != "" { if ociRuntime.Path != "" {
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path)) taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
} }

View File

@ -37,7 +37,6 @@ import (
runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1" runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/reference/docker" "github.com/containerd/containerd/reference/docker"
"github.com/containerd/containerd/runtime/linux/runctypes"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
runtimespec "github.com/opencontainers/runtime-spec/specs-go" runtimespec "github.com/opencontainers/runtime-spec/specs-go"
@ -338,15 +337,7 @@ func parseImageReferences(refs []string) ([]string, []string) {
// generateRuntimeOptions generates runtime options from cri plugin config. // generateRuntimeOptions generates runtime options from cri plugin config.
func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) { func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) {
if r.Options == nil { if r.Options == nil {
if r.Type != plugin.RuntimeLinuxV1 { return nil, nil
return nil, nil
}
// This is a legacy config, generate runctypes.RuncOptions.
return &runctypes.RuncOptions{
Runtime: r.Engine,
RuntimeRoot: r.Root,
SystemdCgroup: c.SystemdCgroup,
}, nil
} }
optionsTree, err := toml.TreeFromMap(r.Options) optionsTree, err := toml.TreeFromMap(r.Options)
if err != nil { if err != nil {
@ -372,12 +363,8 @@ func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{
// getRuntimeOptionsType gets empty runtime options by the runtime type name. // getRuntimeOptionsType gets empty runtime options by the runtime type name.
func getRuntimeOptionsType(t string) interface{} { func getRuntimeOptionsType(t string) interface{} {
switch t { switch t {
case plugin.RuntimeRuncV1:
fallthrough
case plugin.RuntimeRuncV2: case plugin.RuntimeRuncV2:
return &runcoptions.Options{} return &runcoptions.Options{}
case plugin.RuntimeLinuxV1:
return &runctypes.RuncOptions{}
case runtimeRunhcsV1: case runtimeRunhcsV1:
return &runhcsoptions.Options{} return &runhcsoptions.Options{}
default: default:

View File

@ -33,7 +33,6 @@ import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/protobuf/types" "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/reference/docker" "github.com/containerd/containerd/reference/docker"
"github.com/containerd/containerd/runtime/linux/runctypes"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options" runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
@ -206,29 +205,19 @@ func TestLocalResolve(t *testing.T) {
func TestGenerateRuntimeOptions(t *testing.T) { func TestGenerateRuntimeOptions(t *testing.T) {
nilOpts := ` nilOpts := `
systemd_cgroup = true
[containerd] [containerd]
no_pivot = true no_pivot = true
default_runtime_name = "default" default_runtime_name = "default"
[containerd.runtimes.legacy]
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
[containerd.runtimes.runc]
runtime_type = "` + plugin.RuntimeRuncV1 + `"
[containerd.runtimes.runcv2] [containerd.runtimes.runcv2]
runtime_type = "` + plugin.RuntimeRuncV2 + `" runtime_type = "` + plugin.RuntimeRuncV2 + `"
` `
nonNilOpts := ` nonNilOpts := `
systemd_cgroup = true
[containerd] [containerd]
no_pivot = true no_pivot = true
default_runtime_name = "default" default_runtime_name = "default"
[containerd.runtimes.legacy]
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
[containerd.runtimes.legacy.options] [containerd.runtimes.legacy.options]
Runtime = "legacy" Runtime = "legacy"
RuntimeRoot = "/legacy" RuntimeRoot = "/legacy"
[containerd.runtimes.runc]
runtime_type = "` + plugin.RuntimeRuncV1 + `"
[containerd.runtimes.runc.options] [containerd.runtimes.runc.options]
BinaryName = "runc" BinaryName = "runc"
Root = "/runc" Root = "/runc"
@ -245,7 +234,7 @@ systemd_cgroup = true
require.NoError(t, err) require.NoError(t, err)
err = tree.Unmarshal(&nilOptsConfig) err = tree.Unmarshal(&nilOptsConfig)
require.NoError(t, err) require.NoError(t, err)
require.Len(t, nilOptsConfig.Runtimes, 3) require.Len(t, nilOptsConfig.Runtimes, 1)
tree, err = toml.Load(nonNilOpts) tree, err = toml.Load(nonNilOpts)
require.NoError(t, err) require.NoError(t, err)
@ -258,32 +247,11 @@ systemd_cgroup = true
c criconfig.Config c criconfig.Config
expectedOptions interface{} expectedOptions interface{}
}{ }{
"when options is nil, should return nil option for io.containerd.runc.v1": {
r: nilOptsConfig.Runtimes["runc"],
c: nilOptsConfig,
expectedOptions: nil,
},
"when options is nil, should return nil option for io.containerd.runc.v2": { "when options is nil, should return nil option for io.containerd.runc.v2": {
r: nilOptsConfig.Runtimes["runcv2"], r: nilOptsConfig.Runtimes["runcv2"],
c: nilOptsConfig, c: nilOptsConfig,
expectedOptions: nil, expectedOptions: nil,
}, },
"when options is nil, should use legacy fields for legacy runtime": {
r: nilOptsConfig.Runtimes["legacy"],
c: nilOptsConfig,
expectedOptions: &runctypes.RuncOptions{
SystemdCgroup: true,
},
},
"when options is not nil, should be able to decode for io.containerd.runc.v1": {
r: nonNilOptsConfig.Runtimes["runc"],
c: nonNilOptsConfig,
expectedOptions: &runcoptions.Options{
BinaryName: "runc",
Root: "/runc",
NoNewKeyring: true,
},
},
"when options is not nil, should be able to decode for io.containerd.runc.v2": { "when options is not nil, should be able to decode for io.containerd.runc.v2": {
r: nonNilOptsConfig.Runtimes["runcv2"], r: nonNilOptsConfig.Runtimes["runcv2"],
c: nonNilOptsConfig, c: nonNilOptsConfig,
@ -293,14 +261,6 @@ systemd_cgroup = true
NoNewKeyring: true, NoNewKeyring: true,
}, },
}, },
"when options is not nil, should be able to decode for legacy runtime": {
r: nonNilOptsConfig.Runtimes["legacy"],
c: nonNilOptsConfig,
expectedOptions: &runctypes.RuncOptions{
Runtime: "legacy",
RuntimeRoot: "/legacy",
},
},
} { } {
t.Run(desc, func(t *testing.T) { t.Run(desc, func(t *testing.T) {
opts, err := generateRuntimeOptions(test.r, test.c) opts, err := generateRuntimeOptions(test.r, test.c)

View File

@ -352,7 +352,7 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).", log.G(ctx).Tracef("Create sandbox container (id=%q, name=%q).",
id, name) id, name)
taskOpts := c.taskOpts(ociRuntime.Type) var taskOpts []containerd.NewTaskOpts
if ociRuntime.Path != "" { if ociRuntime.Path != "" {
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path)) taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
} }

View File

@ -22,9 +22,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
runtimespec "github.com/opencontainers/runtime-spec/specs-go" runtimespec "github.com/opencontainers/runtime-spec/specs-go"
@ -344,22 +342,6 @@ func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
return nil return nil
} }
// taskOpts generates task options for a (sandbox) container.
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
// TODO(random-liu): Remove this after shim v1 is deprecated.
var taskOpts []containerd.NewTaskOpts
// c.config.NoPivot is only supported for RuntimeLinuxV1 = "io.containerd.runtime.v1.linux" legacy linux runtime
// and is not supported for RuntimeRuncV1 = "io.containerd.runc.v1" or RuntimeRuncV2 = "io.containerd.runc.v2"
// for RuncV1/2 no pivot is set under the containerd.runtimes.runc.options config see
// https://github.com/containerd/containerd/blob/v1.3.2/runtime/v2/runc/options/oci.pb.go#L26
if c.config.NoPivot && runtimeType == plugin.RuntimeLinuxV1 {
taskOpts = append(taskOpts, containerd.WithNoPivotRoot)
}
return taskOpts
}
func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) { func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) {
for i := range spec.Linux.Namespaces { for i := range spec.Linux.Namespaces {
if spec.Linux.Namespaces[i].Type == runtimespec.NetworkNamespace { if spec.Linux.Namespaces[i].Type == runtimespec.NetworkNamespace {

View File

@ -19,7 +19,6 @@
package server package server
import ( import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/pkg/cri/annotations" "github.com/containerd/containerd/pkg/cri/annotations"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
@ -51,11 +50,6 @@ func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
return nil return nil
} }
// taskOpts generates task options for a (sandbox) container.
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
return []containerd.NewTaskOpts{}
}
func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) { func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) {
} }

View File

@ -29,8 +29,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1" runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"github.com/containerd/containerd/pkg/cri/annotations"
criconfig "github.com/containerd/containerd/pkg/cri/config"
sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox" sandboxstore "github.com/containerd/containerd/pkg/cri/store/sandbox"
) )
@ -361,166 +359,3 @@ func TestHostAccessingSandbox(t *testing.T) {
}) })
} }
} }
func TestGetSandboxRuntime(t *testing.T) {
untrustedWorkloadRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "untrusted-workload-runtime",
Root: "",
}
defaultRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "default-runtime",
Root: "",
}
fooRuntime := criconfig.Runtime{
Type: "io.containerd.runtime.v1.linux",
Engine: "foo-bar",
Root: "",
}
for desc, test := range map[string]struct {
sandboxConfig *runtime.PodSandboxConfig
runtimeHandler string
runtimes map[string]criconfig.Runtime
expectErr bool
expectedRuntime criconfig.Runtime
}{
"should return error if untrusted workload requires host access": {
sandboxConfig: &runtime.PodSandboxConfig{
Linux: &runtime.LinuxPodSandboxConfig{
SecurityContext: &runtime.LinuxSandboxSecurityContext{
Privileged: false,
NamespaceOptions: &runtime.NamespaceOption{
Network: runtime.NamespaceMode_NODE,
Pid: runtime.NamespaceMode_NODE,
Ipc: runtime.NamespaceMode_NODE,
},
},
},
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectErr: true,
},
"should use untrusted workload runtime for untrusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: untrustedWorkloadRuntime,
},
"should use default runtime for regular workload": {
sandboxConfig: &runtime.PodSandboxConfig{},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
},
expectedRuntime: defaultRuntime,
},
"should use default runtime for trusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "false",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: defaultRuntime,
},
"should return error if untrusted workload runtime is required but not configured": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
},
expectErr: true,
},
"should use 'untrusted' runtime for untrusted workload": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: untrustedWorkloadRuntime,
},
"should use 'untrusted' runtime for untrusted workload & handler": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimeHandler: "untrusted",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
},
expectedRuntime: untrustedWorkloadRuntime,
},
"should return an error if untrusted annotation with conflicting handler": {
sandboxConfig: &runtime.PodSandboxConfig{
Annotations: map[string]string{
annotations.UntrustedWorkload: "true",
},
},
runtimeHandler: "foo",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
"foo": fooRuntime,
},
expectErr: true,
},
"should use correct runtime for a runtime handler": {
sandboxConfig: &runtime.PodSandboxConfig{},
runtimeHandler: "foo",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
criconfig.RuntimeUntrusted: untrustedWorkloadRuntime,
"foo": fooRuntime,
},
expectedRuntime: fooRuntime,
},
"should return error if runtime handler is required but not configured": {
sandboxConfig: &runtime.PodSandboxConfig{},
runtimeHandler: "bar",
runtimes: map[string]criconfig.Runtime{
criconfig.RuntimeDefault: defaultRuntime,
"foo": fooRuntime,
},
expectErr: true,
},
} {
t.Run(desc, func(t *testing.T) {
cri := newTestCRIService()
cri.config = criconfig.Config{
PluginConfig: criconfig.DefaultConfig(),
}
cri.config.ContainerdConfig.DefaultRuntimeName = criconfig.RuntimeDefault
cri.config.ContainerdConfig.Runtimes = test.runtimes
r, err := cri.getSandboxRuntime(test.sandboxConfig, test.runtimeHandler)
assert.Equal(t, test.expectErr, err != nil)
assert.Equal(t, test.expectedRuntime, r)
})
}
}

View File

@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/containerd/containerd"
"github.com/containerd/containerd/oci" "github.com/containerd/containerd/oci"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
@ -104,11 +103,6 @@ func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
return nil return nil
} }
// No task options needed for windows.
func (c *criService) taskOpts(runtimeType string) []containerd.NewTaskOpts {
return nil
}
func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) { func (c *criService) updateNetNamespacePath(spec *runtimespec.Spec, nsPath string) {
spec.Windows.Network.NetworkNamespace = nsPath spec.Windows.Network.NetworkNamespace = nsPath
} }

View File

@ -93,10 +93,6 @@ const (
) )
const ( const (
// RuntimeLinuxV1 is the legacy linux runtime
RuntimeLinuxV1 = "io.containerd.runtime.v1.linux"
// RuntimeRuncV1 is the runc runtime that supports a single container
RuntimeRuncV1 = "io.containerd.runc.v1"
// RuntimeRuncV2 is the runc runtime that supports multiple containers per shim // RuntimeRuncV2 is the runc runtime that supports multiple containers per shim
RuntimeRuncV2 = "io.containerd.runc.v2" RuntimeRuncV2 = "io.containerd.runc.v2"
) )

View File

@ -1,183 +0,0 @@
file {
name: "github.com/containerd/containerd/linux/runctypes/runc.proto"
package: "containerd.linux.runc"
dependency: "gogoproto/gogo.proto"
message_type {
name: "RuncOptions"
field {
name: "runtime"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "runtime"
}
field {
name: "runtime_root"
number: 2
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "runtimeRoot"
}
field {
name: "criu_path"
number: 3
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuPath"
}
field {
name: "systemd_cgroup"
number: 4
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "systemdCgroup"
}
}
message_type {
name: "CreateOptions"
field {
name: "no_pivot_root"
number: 1
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "noPivotRoot"
}
field {
name: "open_tcp"
number: 2
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "openTcp"
}
field {
name: "external_unix_sockets"
number: 3
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "externalUnixSockets"
}
field {
name: "terminal"
number: 4
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "terminal"
}
field {
name: "file_locks"
number: 5
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "fileLocks"
}
field {
name: "empty_namespaces"
number: 6
label: LABEL_REPEATED
type: TYPE_STRING
json_name: "emptyNamespaces"
}
field {
name: "cgroups_mode"
number: 7
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "cgroupsMode"
}
field {
name: "no_new_keyring"
number: 8
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "noNewKeyring"
}
field {
name: "shim_cgroup"
number: 9
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "shimCgroup"
}
field {
name: "io_uid"
number: 10
label: LABEL_OPTIONAL
type: TYPE_UINT32
json_name: "ioUid"
}
field {
name: "io_gid"
number: 11
label: LABEL_OPTIONAL
type: TYPE_UINT32
json_name: "ioGid"
}
}
message_type {
name: "CheckpointOptions"
field {
name: "exit"
number: 1
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "exit"
}
field {
name: "open_tcp"
number: 2
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "openTcp"
}
field {
name: "external_unix_sockets"
number: 3
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "externalUnixSockets"
}
field {
name: "terminal"
number: 4
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "terminal"
}
field {
name: "file_locks"
number: 5
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "fileLocks"
}
field {
name: "empty_namespaces"
number: 6
label: LABEL_REPEATED
type: TYPE_STRING
json_name: "emptyNamespaces"
}
field {
name: "cgroups_mode"
number: 7
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "cgroupsMode"
}
}
message_type {
name: "ProcessDetails"
field {
name: "exec_id"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "execId"
}
}
options {
go_package: "github.com/containerd/containerd/linux/runctypes;runctypes"
}
weak_dependency: 0
syntax: "proto3"
}

View File

@ -1,17 +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 runctypes

View File

@ -1,212 +0,0 @@
file {
name: "github.com/containerd/containerd/runtime/linux/runctypes/runc.proto"
package: "containerd.linux.runc"
message_type {
name: "RuncOptions"
field {
name: "runtime"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "runtime"
}
field {
name: "runtime_root"
number: 2
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "runtimeRoot"
}
field {
name: "criu_path"
number: 3
label: LABEL_OPTIONAL
type: TYPE_STRING
options {
deprecated: true
}
json_name: "criuPath"
}
field {
name: "systemd_cgroup"
number: 4
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "systemdCgroup"
}
}
message_type {
name: "CreateOptions"
field {
name: "no_pivot_root"
number: 1
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "noPivotRoot"
}
field {
name: "open_tcp"
number: 2
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "openTcp"
}
field {
name: "external_unix_sockets"
number: 3
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "externalUnixSockets"
}
field {
name: "terminal"
number: 4
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "terminal"
}
field {
name: "file_locks"
number: 5
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "fileLocks"
}
field {
name: "empty_namespaces"
number: 6
label: LABEL_REPEATED
type: TYPE_STRING
json_name: "emptyNamespaces"
}
field {
name: "cgroups_mode"
number: 7
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "cgroupsMode"
}
field {
name: "no_new_keyring"
number: 8
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "noNewKeyring"
}
field {
name: "shim_cgroup"
number: 9
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "shimCgroup"
}
field {
name: "io_uid"
number: 10
label: LABEL_OPTIONAL
type: TYPE_UINT32
json_name: "ioUid"
}
field {
name: "io_gid"
number: 11
label: LABEL_OPTIONAL
type: TYPE_UINT32
json_name: "ioGid"
}
field {
name: "criu_work_path"
number: 12
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuWorkPath"
}
field {
name: "criu_image_path"
number: 13
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "criuImagePath"
}
}
message_type {
name: "CheckpointOptions"
field {
name: "exit"
number: 1
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "exit"
}
field {
name: "open_tcp"
number: 2
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "openTcp"
}
field {
name: "external_unix_sockets"
number: 3
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "externalUnixSockets"
}
field {
name: "terminal"
number: 4
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "terminal"
}
field {
name: "file_locks"
number: 5
label: LABEL_OPTIONAL
type: TYPE_BOOL
json_name: "fileLocks"
}
field {
name: "empty_namespaces"
number: 6
label: LABEL_REPEATED
type: TYPE_STRING
json_name: "emptyNamespaces"
}
field {
name: "cgroups_mode"
number: 7
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "cgroupsMode"
}
field {
name: "work_path"
number: 8
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "workPath"
}
field {
name: "image_path"
number: 9
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "imagePath"
}
}
message_type {
name: "ProcessDetails"
field {
name: "exec_id"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "execId"
}
}
options {
go_package: "github.com/containerd/containerd/runtime/linux/runctypes;runctypes"
}
syntax: "proto3"
}

View File

@ -1,581 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.20.1
// source: github.com/containerd/containerd/runtime/linux/runctypes/runc.proto
package runctypes
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type RuncOptions struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Runtime string `protobuf:"bytes,1,opt,name=runtime,proto3" json:"runtime,omitempty"`
RuntimeRoot string `protobuf:"bytes,2,opt,name=runtime_root,json=runtimeRoot,proto3" json:"runtime_root,omitempty"`
// criu binary path.
//
// 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.
//
// Deprecated: Do not use.
CriuPath string `protobuf:"bytes,3,opt,name=criu_path,json=criuPath,proto3" json:"criu_path,omitempty"`
SystemdCgroup bool `protobuf:"varint,4,opt,name=systemd_cgroup,json=systemdCgroup,proto3" json:"systemd_cgroup,omitempty"`
}
func (x *RuncOptions) Reset() {
*x = RuncOptions{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *RuncOptions) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RuncOptions) ProtoMessage() {}
func (x *RuncOptions) ProtoReflect() protoreflect.Message {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RuncOptions.ProtoReflect.Descriptor instead.
func (*RuncOptions) Descriptor() ([]byte, []int) {
return file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescGZIP(), []int{0}
}
func (x *RuncOptions) GetRuntime() string {
if x != nil {
return x.Runtime
}
return ""
}
func (x *RuncOptions) GetRuntimeRoot() string {
if x != nil {
return x.RuntimeRoot
}
return ""
}
// Deprecated: Do not use.
func (x *RuncOptions) GetCriuPath() string {
if x != nil {
return x.CriuPath
}
return ""
}
func (x *RuncOptions) GetSystemdCgroup() bool {
if x != nil {
return x.SystemdCgroup
}
return false
}
type CreateOptions struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
NoPivotRoot bool `protobuf:"varint,1,opt,name=no_pivot_root,json=noPivotRoot,proto3" json:"no_pivot_root,omitempty"`
OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"`
ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"`
Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"`
FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"`
EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces,proto3" json:"empty_namespaces,omitempty"`
CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"`
NoNewKeyring bool `protobuf:"varint,8,opt,name=no_new_keyring,json=noNewKeyring,proto3" json:"no_new_keyring,omitempty"`
ShimCgroup string `protobuf:"bytes,9,opt,name=shim_cgroup,json=shimCgroup,proto3" json:"shim_cgroup,omitempty"`
IoUid uint32 `protobuf:"varint,10,opt,name=io_uid,json=ioUid,proto3" json:"io_uid,omitempty"`
IoGid uint32 `protobuf:"varint,11,opt,name=io_gid,json=ioGid,proto3" json:"io_gid,omitempty"`
CriuWorkPath string `protobuf:"bytes,12,opt,name=criu_work_path,json=criuWorkPath,proto3" json:"criu_work_path,omitempty"`
CriuImagePath string `protobuf:"bytes,13,opt,name=criu_image_path,json=criuImagePath,proto3" json:"criu_image_path,omitempty"`
}
func (x *CreateOptions) Reset() {
*x = CreateOptions{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateOptions) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateOptions) ProtoMessage() {}
func (x *CreateOptions) ProtoReflect() protoreflect.Message {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateOptions.ProtoReflect.Descriptor instead.
func (*CreateOptions) Descriptor() ([]byte, []int) {
return file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescGZIP(), []int{1}
}
func (x *CreateOptions) GetNoPivotRoot() bool {
if x != nil {
return x.NoPivotRoot
}
return false
}
func (x *CreateOptions) GetOpenTcp() bool {
if x != nil {
return x.OpenTcp
}
return false
}
func (x *CreateOptions) GetExternalUnixSockets() bool {
if x != nil {
return x.ExternalUnixSockets
}
return false
}
func (x *CreateOptions) GetTerminal() bool {
if x != nil {
return x.Terminal
}
return false
}
func (x *CreateOptions) GetFileLocks() bool {
if x != nil {
return x.FileLocks
}
return false
}
func (x *CreateOptions) GetEmptyNamespaces() []string {
if x != nil {
return x.EmptyNamespaces
}
return nil
}
func (x *CreateOptions) GetCgroupsMode() string {
if x != nil {
return x.CgroupsMode
}
return ""
}
func (x *CreateOptions) GetNoNewKeyring() bool {
if x != nil {
return x.NoNewKeyring
}
return false
}
func (x *CreateOptions) GetShimCgroup() string {
if x != nil {
return x.ShimCgroup
}
return ""
}
func (x *CreateOptions) GetIoUid() uint32 {
if x != nil {
return x.IoUid
}
return 0
}
func (x *CreateOptions) GetIoGid() uint32 {
if x != nil {
return x.IoGid
}
return 0
}
func (x *CreateOptions) GetCriuWorkPath() string {
if x != nil {
return x.CriuWorkPath
}
return ""
}
func (x *CreateOptions) GetCriuImagePath() string {
if x != nil {
return x.CriuImagePath
}
return ""
}
type CheckpointOptions struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Exit bool `protobuf:"varint,1,opt,name=exit,proto3" json:"exit,omitempty"`
OpenTcp bool `protobuf:"varint,2,opt,name=open_tcp,json=openTcp,proto3" json:"open_tcp,omitempty"`
ExternalUnixSockets bool `protobuf:"varint,3,opt,name=external_unix_sockets,json=externalUnixSockets,proto3" json:"external_unix_sockets,omitempty"`
Terminal bool `protobuf:"varint,4,opt,name=terminal,proto3" json:"terminal,omitempty"`
FileLocks bool `protobuf:"varint,5,opt,name=file_locks,json=fileLocks,proto3" json:"file_locks,omitempty"`
EmptyNamespaces []string `protobuf:"bytes,6,rep,name=empty_namespaces,json=emptyNamespaces,proto3" json:"empty_namespaces,omitempty"`
CgroupsMode string `protobuf:"bytes,7,opt,name=cgroups_mode,json=cgroupsMode,proto3" json:"cgroups_mode,omitempty"`
WorkPath string `protobuf:"bytes,8,opt,name=work_path,json=workPath,proto3" json:"work_path,omitempty"`
ImagePath string `protobuf:"bytes,9,opt,name=image_path,json=imagePath,proto3" json:"image_path,omitempty"`
}
func (x *CheckpointOptions) Reset() {
*x = CheckpointOptions{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CheckpointOptions) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CheckpointOptions) ProtoMessage() {}
func (x *CheckpointOptions) ProtoReflect() protoreflect.Message {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CheckpointOptions.ProtoReflect.Descriptor instead.
func (*CheckpointOptions) Descriptor() ([]byte, []int) {
return file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescGZIP(), []int{2}
}
func (x *CheckpointOptions) GetExit() bool {
if x != nil {
return x.Exit
}
return false
}
func (x *CheckpointOptions) GetOpenTcp() bool {
if x != nil {
return x.OpenTcp
}
return false
}
func (x *CheckpointOptions) GetExternalUnixSockets() bool {
if x != nil {
return x.ExternalUnixSockets
}
return false
}
func (x *CheckpointOptions) GetTerminal() bool {
if x != nil {
return x.Terminal
}
return false
}
func (x *CheckpointOptions) GetFileLocks() bool {
if x != nil {
return x.FileLocks
}
return false
}
func (x *CheckpointOptions) GetEmptyNamespaces() []string {
if x != nil {
return x.EmptyNamespaces
}
return nil
}
func (x *CheckpointOptions) GetCgroupsMode() string {
if x != nil {
return x.CgroupsMode
}
return ""
}
func (x *CheckpointOptions) GetWorkPath() string {
if x != nil {
return x.WorkPath
}
return ""
}
func (x *CheckpointOptions) GetImagePath() string {
if x != nil {
return x.ImagePath
}
return ""
}
type ProcessDetails struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ExecID string `protobuf:"bytes,1,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"`
}
func (x *ProcessDetails) Reset() {
*x = ProcessDetails{}
if protoimpl.UnsafeEnabled {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ProcessDetails) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ProcessDetails) ProtoMessage() {}
func (x *ProcessDetails) ProtoReflect() protoreflect.Message {
mi := &file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ProcessDetails.ProtoReflect.Descriptor instead.
func (*ProcessDetails) Descriptor() ([]byte, []int) {
return file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescGZIP(), []int{3}
}
func (x *ProcessDetails) GetExecID() string {
if x != nil {
return x.ExecID
}
return ""
}
var File_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto protoreflect.FileDescriptor
var file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDesc = []byte{
0x0a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e,
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65,
0x72, 0x64, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78,
0x2f, 0x72, 0x75, 0x6e, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x72, 0x75, 0x6e, 0x63, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
0x64, 0x2e, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2e, 0x72, 0x75, 0x6e, 0x63, 0x22, 0x92, 0x01, 0x0a,
0x0b, 0x52, 0x75, 0x6e, 0x63, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07,
0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72,
0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d,
0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x75,
0x6e, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x09, 0x63, 0x72, 0x69,
0x75, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01,
0x52, 0x08, 0x63, 0x72, 0x69, 0x75, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x79,
0x73, 0x74, 0x65, 0x6d, 0x64, 0x5f, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01,
0x28, 0x08, 0x52, 0x0d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x64, 0x43, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x22, 0xce, 0x03, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x5f, 0x70, 0x69, 0x76, 0x6f, 0x74, 0x5f,
0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x50, 0x69,
0x76, 0x6f, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x70, 0x65, 0x6e, 0x5f,
0x74, 0x63, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6f, 0x70, 0x65, 0x6e, 0x54,
0x63, 0x70, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x75,
0x6e, 0x69, 0x78, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
0x08, 0x52, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x78, 0x53,
0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e,
0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e,
0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x6b, 0x73,
0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x6f, 0x63, 0x6b,
0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73,
0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x65, 0x6d, 0x70,
0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c,
0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4d, 0x6f, 0x64, 0x65, 0x12,
0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x5f, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x72, 0x69, 0x6e,
0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x6f, 0x4e, 0x65, 0x77, 0x4b, 0x65,
0x79, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x68, 0x69, 0x6d, 0x5f, 0x63, 0x67,
0x72, 0x6f, 0x75, 0x70, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x68, 0x69, 0x6d,
0x43, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x15, 0x0a, 0x06, 0x69, 0x6f, 0x5f, 0x75, 0x69, 0x64,
0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6f, 0x55, 0x69, 0x64, 0x12, 0x15, 0x0a,
0x06, 0x69, 0x6f, 0x5f, 0x67, 0x69, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69,
0x6f, 0x47, 0x69, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x63, 0x72, 0x69, 0x75, 0x5f, 0x77, 0x6f, 0x72,
0x6b, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x72,
0x69, 0x75, 0x57, 0x6f, 0x72, 0x6b, 0x50, 0x61, 0x74, 0x68, 0x12, 0x26, 0x0a, 0x0f, 0x63, 0x72,
0x69, 0x75, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0d, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x72, 0x69, 0x75, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x61,
0x74, 0x68, 0x22, 0xbb, 0x02, 0x0a, 0x11, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e,
0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x78, 0x69, 0x74,
0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x65, 0x78, 0x69, 0x74, 0x12, 0x19, 0x0a, 0x08,
0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x74, 0x63, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
0x6f, 0x70, 0x65, 0x6e, 0x54, 0x63, 0x70, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72,
0x6e, 0x61, 0x6c, 0x5f, 0x75, 0x6e, 0x69, 0x78, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73,
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x55, 0x6e, 0x69, 0x78, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x74,
0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x74,
0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x5f,
0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x69, 0x6c,
0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09,
0x52, 0x0f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x73, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x5f, 0x6d, 0x6f, 0x64,
0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73,
0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x77, 0x6f, 0x72, 0x6b, 0x5f, 0x70, 0x61, 0x74,
0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x77, 0x6f, 0x72, 0x6b, 0x50, 0x61, 0x74,
0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18,
0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x61, 0x74, 0x68,
0x22, 0x29, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69,
0x6c, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x78, 0x65, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x06, 0x65, 0x78, 0x65, 0x63, 0x49, 0x64, 0x42, 0x44, 0x5a, 0x42, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69,
0x6e, 0x65, 0x72, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x64, 0x2f,
0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x2f, 0x6c, 0x69, 0x6e, 0x75, 0x78, 0x2f, 0x72, 0x75,
0x6e, 0x63, 0x74, 0x79, 0x70, 0x65, 0x73, 0x3b, 0x72, 0x75, 0x6e, 0x63, 0x74, 0x79, 0x70, 0x65,
0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescOnce sync.Once
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescData = file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDesc
)
func file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescGZIP() []byte {
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescOnce.Do(func() {
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescData)
})
return file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDescData
}
var file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_goTypes = []interface{}{
(*RuncOptions)(nil), // 0: containerd.linux.runc.RuncOptions
(*CreateOptions)(nil), // 1: containerd.linux.runc.CreateOptions
(*CheckpointOptions)(nil), // 2: containerd.linux.runc.CheckpointOptions
(*ProcessDetails)(nil), // 3: containerd.linux.runc.ProcessDetails
}
var file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_init() }
func file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_init() {
if File_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*RuncOptions); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateOptions); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CheckpointOptions); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ProcessDetails); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_goTypes,
DependencyIndexes: file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_depIdxs,
MessageInfos: file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_msgTypes,
}.Build()
File_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto = out.File
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_rawDesc = nil
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_goTypes = nil
file_github_com_containerd_containerd_runtime_linux_runctypes_runc_proto_depIdxs = nil
}

View File

@ -1,50 +0,0 @@
syntax = "proto3";
package containerd.linux.runc;
option go_package = "github.com/containerd/containerd/runtime/linux/runctypes;runctypes";
message RuncOptions {
string runtime = 1;
string runtime_root = 2;
// criu binary path.
//
// 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.
string criu_path = 3 [deprecated = true];
bool systemd_cgroup = 4;
}
message CreateOptions {
bool no_pivot_root = 1;
bool open_tcp = 2;
bool external_unix_sockets = 3;
bool terminal = 4;
bool file_locks = 5;
repeated string empty_namespaces = 6;
string cgroups_mode = 7;
bool no_new_keyring = 8;
string shim_cgroup = 9;
uint32 io_uid = 10;
uint32 io_gid = 11;
string criu_work_path = 12;
string criu_image_path = 13;
}
message CheckpointOptions {
bool exit = 1;
bool open_tcp = 2;
bool external_unix_sockets = 3;
bool terminal = 4;
bool file_locks = 5;
repeated string empty_namespaces = 6;
string cgroups_mode = 7;
string work_path = 8;
string image_path = 9;
}
message ProcessDetails {
string exec_id = 1;
}

View File

@ -1,248 +0,0 @@
//go: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 linux
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v1/shim"
"github.com/containerd/containerd/runtime/v1/shim/client"
"github.com/opencontainers/runtime-spec/specs-go"
)
// loadBundle loads an existing bundle from disk
func loadBundle(id, path, workdir string) *bundle {
return &bundle{
id: id,
path: path,
workDir: workdir,
}
}
// newBundle creates a new bundle on disk at the provided path for the given id
func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
if err := os.MkdirAll(path, 0711); err != nil {
return nil, err
}
path = filepath.Join(path, id)
if err := os.Mkdir(path, 0700); err != nil {
return nil, err
}
defer func() {
if err != nil {
os.RemoveAll(path)
}
}()
if err := prepareBundleDirectoryPermissions(path, spec); err != nil {
return nil, err
}
workDir = filepath.Join(workDir, id)
if err := os.MkdirAll(workDir, 0711); err != nil {
return nil, err
}
defer func() {
if err != nil {
os.RemoveAll(workDir)
}
}()
rootfs := filepath.Join(path, "rootfs")
if err := os.MkdirAll(rootfs, 0711); err != nil {
return nil, err
}
err = os.WriteFile(filepath.Join(path, configFilename), spec, 0666)
return &bundle{
id: id,
path: path,
workDir: workDir,
}, err
}
// prepareBundleDirectoryPermissions prepares the permissions of the bundle
// directory. When user namespaces are enabled, the permissions are modified
// to allow the remapped root GID to access the bundle.
func prepareBundleDirectoryPermissions(path string, spec []byte) error {
gid, err := remappedGID(spec)
if err != nil {
return err
}
if gid == 0 {
return nil
}
if err := os.Chown(path, -1, int(gid)); err != nil {
return err
}
return os.Chmod(path, 0710)
}
// ociSpecUserNS is a subset of specs.Spec used to reduce garbage during
// unmarshal.
type ociSpecUserNS struct {
Linux *linuxSpecUserNS
}
// linuxSpecUserNS is a subset of specs.Linux used to reduce garbage during
// unmarshal.
type linuxSpecUserNS struct {
GIDMappings []specs.LinuxIDMapping
}
// remappedGID reads the remapped GID 0 from the OCI spec, if it exists. If
// there is no remapping, remappedGID returns 0. If the spec cannot be parsed,
// remappedGID returns an error.
func remappedGID(spec []byte) (uint32, error) {
var ociSpec ociSpecUserNS
err := json.Unmarshal(spec, &ociSpec)
if err != nil {
return 0, err
}
if ociSpec.Linux == nil || len(ociSpec.Linux.GIDMappings) == 0 {
return 0, nil
}
for _, mapping := range ociSpec.Linux.GIDMappings {
if mapping.ContainerID == 0 {
return mapping.HostID, nil
}
}
return 0, nil
}
type bundle struct {
id string
path string
workDir string
}
// ShimOpt specifies shim options for initialization and connection
type ShimOpt func(*bundle, string, *runctypes.RuncOptions) (shim.Config, client.Opt)
// ShimRemote is a ShimOpt for connecting and starting a remote shim
func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) ShimOpt {
return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
config := b.shimConfig(ns, c, ropts)
return config,
client.WithStart(c.Shim, b.shimAddress(ns, daemonAddress), daemonAddress, cgroup, c.ShimDebug, exitHandler)
}
}
// ShimLocal is a ShimOpt for using an in process shim implementation
func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt {
return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
return b.shimConfig(ns, c, ropts), client.WithLocal(exchange)
}
}
// ShimConnect is a ShimOpt for connecting to an existing remote shim
func ShimConnect(c *Config, onClose func()) ShimOpt {
return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose)
}
}
// NewShimClient connects to the shim managing the bundle and tasks creating it if needed
func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts ShimOpt, runcOpts *runctypes.RuncOptions) (*client.Client, error) {
cfg, opt := getClientOpts(b, namespace, runcOpts)
return client.New(ctx, cfg, opt)
}
// Delete deletes the bundle from disk
func (b *bundle) Delete() error {
address, _ := b.loadAddress()
if address != "" {
// we don't care about errors here
client.RemoveSocket(address)
}
err := atomicDelete(b.path)
if err == nil {
return atomicDelete(b.workDir)
}
// error removing the bundle path; still attempt removing work dir
err2 := atomicDelete(b.workDir)
if err2 == nil {
return err
}
return fmt.Errorf("failed to remove both bundle and workdir locations: %v: %w", err2, err)
}
func (b *bundle) legacyShimAddress(namespace string) string {
return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock")
}
const socketRoot = "/run/containerd"
func (b *bundle) shimAddress(namespace, socketPath string) string {
d := sha256.Sum256([]byte(filepath.Join(socketPath, namespace, b.id)))
return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d)
}
func (b *bundle) loadAddress() (string, error) {
addressPath := filepath.Join(b.path, "address")
data, err := os.ReadFile(addressPath)
if err != nil {
return "", err
}
return string(data), nil
}
func (b *bundle) decideShimAddress(namespace string) string {
address, err := b.loadAddress()
if err != nil {
return b.legacyShimAddress(namespace)
}
return address
}
func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config {
var (
runtimeRoot = c.RuntimeRoot
systemdCgroup bool
)
if runcOptions != nil {
systemdCgroup = runcOptions.SystemdCgroup
if runcOptions.RuntimeRoot != "" {
runtimeRoot = runcOptions.RuntimeRoot
}
}
return shim.Config{
Path: b.path,
WorkDir: b.workDir,
Namespace: namespace,
RuntimeRoot: runtimeRoot,
SystemdCgroup: systemdCgroup,
}
}
// atomicDelete renames the path to a hidden file before removal
func atomicDelete(path string) error {
// create a hidden dir for an atomic removal
atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
if err := os.Rename(path, atomicPath); err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
return os.RemoveAll(atomicPath)
}

View File

@ -1,141 +0,0 @@
//go: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 linux
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"syscall"
"testing"
"github.com/containerd/containerd/oci"
"github.com/containerd/continuity/testutil"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewBundle(t *testing.T) {
testutil.RequiresRoot(t)
tests := []struct {
userns bool
}{{
userns: false,
}, {
userns: true,
}}
const usernsGID = 4200
for i, tc := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
dir := t.TempDir()
work := filepath.Join(dir, "work")
state := filepath.Join(dir, "state")
id := fmt.Sprintf("new-bundle-%d", i)
spec := oci.Spec{}
if tc.userns {
spec.Linux = &specs.Linux{
GIDMappings: []specs.LinuxIDMapping{{ContainerID: 0, HostID: usernsGID}},
}
}
specBytes, err := json.Marshal(&spec)
require.NoError(t, err, "failed to marshal spec")
b, err := newBundle(id, work, state, specBytes)
require.NoError(t, err, "newBundle should succeed")
require.NotNil(t, b, "bundle should not be nil")
fi, err := os.Stat(b.path)
assert.NoError(t, err, "should be able to stat bundle path")
if tc.userns {
assert.Equal(t, os.ModeDir|0710, fi.Mode(), "bundle path should be a directory with perm 0710")
} else {
assert.Equal(t, os.ModeDir|0700, fi.Mode(), "bundle path should be a directory with perm 0700")
}
stat, ok := fi.Sys().(*syscall.Stat_t)
require.True(t, ok, "should assert to *syscall.Stat_t")
expectedGID := uint32(0)
if tc.userns {
expectedGID = usernsGID
}
assert.Equal(t, expectedGID, stat.Gid, "gid should match")
})
}
}
func TestRemappedGID(t *testing.T) {
tests := []struct {
spec oci.Spec
gid uint32
}{{
// empty spec
spec: oci.Spec{},
gid: 0,
}, {
// empty Linux section
spec: oci.Spec{
Linux: &specs.Linux{},
},
gid: 0,
}, {
// empty ID mappings
spec: oci.Spec{
Linux: &specs.Linux{
GIDMappings: make([]specs.LinuxIDMapping, 0),
},
},
gid: 0,
}, {
// valid ID mapping
spec: oci.Spec{
Linux: &specs.Linux{
GIDMappings: []specs.LinuxIDMapping{{
ContainerID: 0,
HostID: 1000,
}},
},
},
gid: 1000,
}, {
// missing ID mapping
spec: oci.Spec{
Linux: &specs.Linux{
GIDMappings: []specs.LinuxIDMapping{{
ContainerID: 100,
HostID: 1000,
}},
},
},
gid: 0,
}}
for i, tc := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
s, err := json.Marshal(tc.spec)
require.NoError(t, err, "failed to marshal spec")
gid, err := remappedGID(s)
assert.NoError(t, err, "should unmarshal successfully")
assert.Equal(t, tc.gid, gid, "expected GID to match")
})
}
}

View File

@ -1,171 +0,0 @@
//go: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 linux
import (
"context"
"errors"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/api/types/task"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/protobuf"
"github.com/containerd/containerd/runtime"
shim "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/ttrpc"
)
// Process implements a linux process
type Process struct {
id string
t *Task
}
// ID of the process
func (p *Process) ID() string {
return p.id
}
// Kill sends the provided signal to the underlying process
//
// Unable to kill all processes in the task using this method on a process
func (p *Process) Kill(ctx context.Context, signal uint32, _ bool) error {
_, err := p.t.shim.Kill(ctx, &shim.KillRequest{
Signal: signal,
ID: p.id,
})
if err != nil {
return errdefs.FromGRPC(err)
}
return err
}
func statusFromProto(from task.Status) runtime.Status {
var status runtime.Status
switch from {
case task.Status_CREATED:
status = runtime.CreatedStatus
case task.Status_RUNNING:
status = runtime.RunningStatus
case task.Status_STOPPED:
status = runtime.StoppedStatus
case task.Status_PAUSED:
status = runtime.PausedStatus
case task.Status_PAUSING:
status = runtime.PausingStatus
}
return status
}
// State of process
func (p *Process) State(ctx context.Context) (runtime.State, error) {
// use the container status for the status of the process
response, err := p.t.shim.State(ctx, &shim.StateRequest{
ID: p.id,
})
if err != nil {
if !errors.Is(err, ttrpc.ErrClosed) {
return runtime.State{}, errdefs.FromGRPC(err)
}
// We treat ttrpc.ErrClosed as the shim being closed, but really this
// likely means that the process no longer exists. We'll have to plumb
// the connection differently if this causes problems.
return runtime.State{}, errdefs.ErrNotFound
}
return runtime.State{
Pid: response.Pid,
Status: statusFromProto(response.Status),
Stdin: response.Stdin,
Stdout: response.Stdout,
Stderr: response.Stderr,
Terminal: response.Terminal,
ExitStatus: response.ExitStatus,
}, nil
}
// ResizePty changes the side of the process's PTY to the provided width and height
func (p *Process) ResizePty(ctx context.Context, size runtime.ConsoleSize) error {
_, err := p.t.shim.ResizePty(ctx, &shim.ResizePtyRequest{
ID: p.id,
Width: size.Width,
Height: size.Height,
})
if err != nil {
err = errdefs.FromGRPC(err)
}
return err
}
// CloseIO closes the provided IO pipe for the process
func (p *Process) CloseIO(ctx context.Context) error {
_, err := p.t.shim.CloseIO(ctx, &shim.CloseIORequest{
ID: p.id,
Stdin: true,
})
if err != nil {
return errdefs.FromGRPC(err)
}
return nil
}
// Start the process
func (p *Process) Start(ctx context.Context) error {
r, err := p.t.shim.Start(ctx, &shim.StartRequest{
ID: p.id,
})
if err != nil {
return errdefs.FromGRPC(err)
}
p.t.events.Publish(ctx, runtime.TaskExecStartedEventTopic, &eventstypes.TaskExecStarted{
ContainerID: p.t.id,
Pid: r.Pid,
ExecID: p.id,
})
return nil
}
// Wait on the process to exit and return the exit status and timestamp
func (p *Process) Wait(ctx context.Context) (*runtime.Exit, error) {
r, err := p.t.shim.Wait(ctx, &shim.WaitRequest{
ID: p.id,
})
if err != nil {
return nil, err
}
return &runtime.Exit{
Timestamp: protobuf.FromTimestamp(r.ExitedAt),
Status: r.ExitStatus,
}, nil
}
// Delete the process and return the exit status
func (p *Process) Delete(ctx context.Context) (*runtime.Exit, error) {
r, err := p.t.shim.DeleteProcess(ctx, &shim.DeleteProcessRequest{
ID: p.id,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return &runtime.Exit{
Status: r.ExitStatus,
Timestamp: protobuf.FromTimestamp(r.ExitedAt),
Pid: r.Pid,
}, nil
}

View File

@ -1,546 +0,0 @@
//go: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 linux
import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"time"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/cleanup"
"github.com/containerd/containerd/pkg/process"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/protobuf"
ptypes "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/runctypes"
v1 "github.com/containerd/containerd/runtime/v1"
"github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/go-runc"
"github.com/containerd/typeurl/v2"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"golang.org/x/sys/unix"
)
var (
pluginID = fmt.Sprintf("%s.%s", plugin.RuntimePlugin, "linux")
empty = &ptypes.Empty{}
)
const (
configFilename = "config.json"
defaultRuntime = "runc"
defaultShim = "containerd-shim"
// cleanupTimeout is default timeout for cleanup operations
cleanupTimeout = 1 * time.Minute
)
func init() {
plugin.Register(&plugin.Registration{
Type: plugin.RuntimePlugin,
ID: "linux",
InitFn: New,
Requires: []plugin.Type{
plugin.EventPlugin,
plugin.MetadataPlugin,
},
Config: &Config{
Shim: defaultShim,
Runtime: defaultRuntime,
},
})
}
var _ = (runtime.PlatformRuntime)(&Runtime{})
// Config options for the runtime
type Config struct {
// Shim is a path or name of binary implementing the Shim GRPC API
Shim string `toml:"shim"`
// Runtime is a path or name of an OCI runtime used by the shim
Runtime string `toml:"runtime"`
// RuntimeRoot is the path that shall be used by the OCI runtime for its data
RuntimeRoot string `toml:"runtime_root"`
// NoShim calls runc directly from within the pkg
NoShim bool `toml:"no_shim"`
// Debug enable debug on the shim
ShimDebug bool `toml:"shim_debug"`
}
// New returns a configured runtime
func New(ic *plugin.InitContext) (interface{}, error) {
ic.Meta.Platforms = []ocispec.Platform{platforms.DefaultSpec()}
if err := os.MkdirAll(ic.Root, 0711); err != nil {
return nil, err
}
if err := os.MkdirAll(ic.State, 0711); err != nil {
return nil, err
}
m, err := ic.Get(plugin.MetadataPlugin)
if err != nil {
return nil, err
}
ep, err := ic.GetByID(plugin.EventPlugin, "exchange")
if err != nil {
return nil, err
}
cfg := ic.Config.(*Config)
r := &Runtime{
root: ic.Root,
state: ic.State,
tasks: runtime.NewNSMap[runtime.Task](),
containers: metadata.NewContainerStore(m.(*metadata.DB)),
address: ic.Address,
events: ep.(*exchange.Exchange),
config: cfg,
}
tasks, err := r.restoreTasks(ic.Context)
if err != nil {
return nil, err
}
for _, t := range tasks {
if err := r.tasks.AddWithNamespace(t.namespace, t); err != nil {
return nil, err
}
}
return r, nil
}
// Runtime for a linux based system
type Runtime struct {
root string
state string
address string
tasks *runtime.NSMap[runtime.Task]
containers containers.Store
events *exchange.Exchange
config *Config
}
// ID of the runtime
func (r *Runtime) ID() string {
return pluginID
}
// Create a new task
func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts) (_ runtime.Task, err error) {
namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
ctx = log.WithLogger(ctx, log.G(ctx).WithError(err).WithFields(log.Fields{
"id": id,
"namespace": namespace,
}))
if err := identifiers.Validate(id); err != nil {
return nil, fmt.Errorf("invalid task id: %w", err)
}
ropts, err := r.getRuncOptions(ctx, id)
if err != nil {
return nil, err
}
bundle, err := newBundle(id,
filepath.Join(r.state, namespace),
filepath.Join(r.root, namespace),
opts.Spec.GetValue())
if err != nil {
return nil, err
}
defer func() {
if err != nil {
bundle.Delete()
}
}()
shimopt := ShimLocal(r.config, r.events)
if !r.config.NoShim {
var cgroup string
if opts.TaskOptions != nil && opts.TaskOptions.GetValue() != nil {
v, err := typeurl.UnmarshalAny(opts.TaskOptions)
if err != nil {
return nil, err
}
cgroup = v.(*runctypes.CreateOptions).ShimCgroup
}
exitHandler := func() {
log.G(ctx).WithField("id", id).Info("shim reaped")
if _, err := r.tasks.Get(ctx, id); err != nil {
// Task was never started or was already successfully deleted
return
}
if err = r.cleanupAfterDeadShim(cleanup.Background(ctx), bundle, namespace, id); err != nil {
log.G(ctx).WithError(err).Warn("failed to clean up after killed shim")
}
}
shimopt = ShimRemote(r.config, r.address, cgroup, exitHandler)
}
s, err := bundle.NewShimClient(ctx, namespace, shimopt, ropts)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
deferCtx, deferCancel := context.WithTimeout(cleanup.Background(ctx), cleanupTimeout)
defer deferCancel()
if kerr := s.KillShim(deferCtx); kerr != nil {
log.G(ctx).WithError(kerr).Error("failed to kill shim")
}
}
}()
rt := r.config.Runtime
if ropts != nil && ropts.Runtime != "" {
rt = ropts.Runtime
}
sopts := &shim.CreateTaskRequest{
ID: id,
Bundle: bundle.path,
Runtime: rt,
Stdin: opts.IO.Stdin,
Stdout: opts.IO.Stdout,
Stderr: opts.IO.Stderr,
Terminal: opts.IO.Terminal,
Checkpoint: opts.Checkpoint,
Options: protobuf.FromAny(opts.TaskOptions),
}
for _, m := range opts.Rootfs {
sopts.Rootfs = append(sopts.Rootfs, &types.Mount{
Type: m.Type,
Source: m.Source,
Target: m.Target,
Options: m.Options,
})
}
cr, err := s.Create(ctx, sopts)
if err != nil {
return nil, errdefs.FromGRPC(err)
}
t, err := newTask(id, namespace, int(cr.Pid), s, r.events, r.tasks, bundle)
if err != nil {
return nil, err
}
if err := r.tasks.Add(ctx, t); err != nil {
return nil, err
}
r.events.Publish(ctx, runtime.TaskCreateEventTopic, &eventstypes.TaskCreate{
ContainerID: sopts.ID,
Bundle: sopts.Bundle,
Rootfs: sopts.Rootfs,
IO: &eventstypes.TaskIO{
Stdin: sopts.Stdin,
Stdout: sopts.Stdout,
Stderr: sopts.Stderr,
Terminal: sopts.Terminal,
},
Checkpoint: sopts.Checkpoint,
Pid: uint32(t.pid),
})
return t, nil
}
// Tasks returns all tasks known to the runtime
func (r *Runtime) Tasks(ctx context.Context, all bool) ([]runtime.Task, error) {
return r.tasks.GetAll(ctx, all)
}
func (r *Runtime) restoreTasks(ctx context.Context) ([]*Task, error) {
dir, err := os.ReadDir(r.state)
if err != nil {
return nil, err
}
var o []*Task
for _, namespace := range dir {
if !namespace.IsDir() {
continue
}
name := namespace.Name()
// skip hidden directories
if len(name) > 0 && name[0] == '.' {
continue
}
log.G(ctx).WithField("namespace", name).Debug("loading tasks in namespace")
tasks, err := r.loadTasks(ctx, name)
if err != nil {
return nil, err
}
o = append(o, tasks...)
}
return o, nil
}
// Get a specific task by task id
func (r *Runtime) Get(ctx context.Context, id string) (runtime.Task, error) {
return r.tasks.Get(ctx, id)
}
// Add a runtime task
func (r *Runtime) Add(ctx context.Context, task runtime.Task) error {
return r.tasks.Add(ctx, task)
}
// Delete a runtime task
func (r *Runtime) Delete(ctx context.Context, id string) (*runtime.Exit, error) {
task, err := r.tasks.Get(ctx, id)
if err != nil {
return nil, err
}
s := task.(*Task)
exit, err := s.Delete(ctx)
if err != nil {
return nil, err
}
r.tasks.Delete(ctx, id)
return exit, nil
}
func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
dir, err := os.ReadDir(filepath.Join(r.state, ns))
if err != nil {
return nil, err
}
var o []*Task
for _, path := range dir {
if !path.IsDir() {
continue
}
id := path.Name()
// skip hidden directories
if len(id) > 0 && id[0] == '.' {
continue
}
bundle := loadBundle(
id,
filepath.Join(r.state, ns, id),
filepath.Join(r.root, ns, id),
)
ctx = namespaces.WithNamespace(ctx, ns)
ctx = log.WithLogger(ctx, log.G(ctx).WithError(err).WithFields(log.Fields{
"id": id,
"namespace": ns,
}))
pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, process.InitPidFile))
shimExit := make(chan struct{})
s, err := bundle.NewShimClient(ctx, ns, ShimConnect(r.config, func() {
defer close(shimExit)
if _, err := r.tasks.Get(ctx, id); err != nil {
// Task was never started or was already successfully deleted
return
}
if err := r.cleanupAfterDeadShim(ctx, bundle, ns, id); err != nil {
log.G(ctx).WithError(err).WithField("bundle", bundle.path).
Error("cleaning up after dead shim")
}
}), nil)
if err != nil {
log.G(ctx).WithError(err).Error("connecting to shim")
err := r.cleanupAfterDeadShim(ctx, bundle, ns, id)
if err != nil {
log.G(ctx).WithError(err).WithField("bundle", bundle.path).
Error("cleaning up after dead shim")
}
continue
}
logDirPath := filepath.Join(r.root, ns, id)
copyAndClose := func(dst io.Writer, src io.ReadWriteCloser) {
copyDone := make(chan struct{})
go func() {
io.Copy(dst, src)
close(copyDone)
}()
select {
case <-shimExit:
case <-copyDone:
}
src.Close()
}
shimStdoutLog, err := v1.OpenShimStdoutLog(ctx, logDirPath)
if err != nil {
log.G(ctx).WithError(err).WithField("logDirPath", logDirPath).
Error("opening shim stdout log pipe")
continue
}
if r.config.ShimDebug {
go copyAndClose(os.Stdout, shimStdoutLog)
} else {
go copyAndClose(io.Discard, shimStdoutLog)
}
shimStderrLog, err := v1.OpenShimStderrLog(ctx, logDirPath)
if err != nil {
log.G(ctx).WithError(err).WithField("logDirPath", logDirPath).
Error("opening shim stderr log pipe")
continue
}
if r.config.ShimDebug {
go copyAndClose(os.Stderr, shimStderrLog)
} else {
go copyAndClose(io.Discard, shimStderrLog)
}
t, err := newTask(id, ns, pid, s, r.events, r.tasks, bundle)
if err != nil {
log.G(ctx).WithError(err).Error("loading task type")
continue
}
o = append(o, t)
}
return o, nil
}
func (r *Runtime) cleanupAfterDeadShim(ctx context.Context, bundle *bundle, ns, id string) error {
log.G(ctx).Warn("cleaning up after shim dead")
pid, _ := runc.ReadPidFile(filepath.Join(bundle.path, process.InitPidFile))
if err := r.terminate(ctx, bundle, ns, id); err != nil {
if r.config.ShimDebug {
return fmt.Errorf("failed to terminate task, leaving bundle for debugging: %w", err)
}
log.G(ctx).WithError(err).Warn("failed to terminate task")
}
// Notify Client
exitedAt := time.Now().UTC()
r.events.Publish(ctx, runtime.TaskExitEventTopic, &eventstypes.TaskExit{
ContainerID: id,
ID: id,
Pid: uint32(pid),
ExitStatus: 128 + uint32(unix.SIGKILL),
ExitedAt: protobuf.ToTimestamp(exitedAt),
})
r.tasks.Delete(ctx, id)
if err := bundle.Delete(); err != nil {
log.G(ctx).WithError(err).Error("delete bundle")
}
// kill shim
if shimPid, err := runc.ReadPidFile(filepath.Join(bundle.path, "shim.pid")); err == nil && shimPid > 0 {
unix.Kill(shimPid, unix.SIGKILL)
}
r.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{
ContainerID: id,
Pid: uint32(pid),
ExitStatus: 128 + uint32(unix.SIGKILL),
ExitedAt: protobuf.ToTimestamp(exitedAt),
})
return nil
}
func (r *Runtime) terminate(ctx context.Context, bundle *bundle, ns, id string) error {
rt, err := r.getRuntime(ctx, ns, id)
if err != nil {
return err
}
if err := rt.Delete(ctx, id, &runc.DeleteOpts{
Force: true,
}); err != nil {
log.G(ctx).WithError(err).Warnf("delete runtime state %s", id)
}
if err := mount.Unmount(filepath.Join(bundle.path, "rootfs"), 0); err != nil {
log.G(ctx).WithError(err).WithFields(log.Fields{
"path": bundle.path,
"id": id,
}).Warnf("unmount task rootfs")
}
return nil
}
func (r *Runtime) getRuntime(ctx context.Context, ns, id string) (*runc.Runc, error) {
ropts, err := r.getRuncOptions(ctx, id)
if err != nil {
return nil, err
}
var (
cmd = r.config.Runtime
root = process.RuncRoot
)
if ropts != nil {
if ropts.Runtime != "" {
cmd = ropts.Runtime
}
if ropts.RuntimeRoot != "" {
root = ropts.RuntimeRoot
}
}
return &runc.Runc{
Command: cmd,
LogFormat: runc.JSON,
PdeathSignal: unix.SIGKILL,
Root: filepath.Join(root, ns),
Debug: r.config.ShimDebug,
}, nil
}
func (r *Runtime) getRuncOptions(ctx context.Context, id string) (*runctypes.RuncOptions, error) {
container, err := r.containers.Get(ctx, id)
if err != nil {
return nil, err
}
if container.Runtime.Options != nil && container.Runtime.Options.GetValue() != nil {
v, err := typeurl.UnmarshalAny(container.Runtime.Options)
if err != nil {
return nil, err
}
ropts, ok := v.(*runctypes.RuncOptions)
if !ok {
return nil, errors.New("invalid runtime options format")
}
return ropts, nil
}
return &runctypes.RuncOptions{}, nil
}

View File

@ -1,353 +0,0 @@
//go: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 linux
import (
"context"
"errors"
"fmt"
"sync"
cgroups "github.com/containerd/cgroups/v3/cgroup1"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/protobuf"
"github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/v1/shim/client"
"github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/ttrpc"
)
// Task on a linux based system
type Task struct {
mu sync.Mutex
id string
pid int
shim *client.Client
namespace string
cg cgroups.Cgroup
events *exchange.Exchange
tasks *runtime.NSMap[runtime.Task]
bundle *bundle
}
func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, list *runtime.NSMap[runtime.Task], bundle *bundle) (*Task, error) {
var (
err error
cg cgroups.Cgroup
)
if pid > 0 {
cg, err = cgroups.Load(cgroups.PidPath(pid))
if err != nil && err != cgroups.ErrCgroupDeleted {
return nil, err
}
}
return &Task{
id: id,
pid: pid,
shim: shim,
namespace: namespace,
cg: cg,
events: events,
tasks: list,
bundle: bundle,
}, nil
}
// ID of the task
func (t *Task) ID() string {
return t.id
}
// Namespace of the task
func (t *Task) Namespace() string {
return t.namespace
}
// PID of the task
func (t *Task) PID(_ context.Context) (uint32, error) {
return uint32(t.pid), nil
}
// Delete the task and return the exit status
func (t *Task) Delete(ctx context.Context) (*runtime.Exit, error) {
rsp, shimErr := t.shim.Delete(ctx, empty)
if shimErr != nil {
shimErr = errdefs.FromGRPC(shimErr)
if !errdefs.IsNotFound(shimErr) {
return nil, shimErr
}
}
t.tasks.Delete(ctx, t.id)
if err := t.shim.KillShim(ctx); err != nil {
log.G(ctx).WithError(err).Error("failed to kill shim")
}
if err := t.bundle.Delete(); err != nil {
log.G(ctx).WithError(err).Error("failed to delete bundle")
}
if shimErr != nil {
return nil, shimErr
}
t.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{
ContainerID: t.id,
ExitStatus: rsp.ExitStatus,
ExitedAt: rsp.ExitedAt,
Pid: rsp.Pid,
})
return &runtime.Exit{
Status: rsp.ExitStatus,
Timestamp: protobuf.FromTimestamp(rsp.ExitedAt),
Pid: rsp.Pid,
}, nil
}
// Start the task
func (t *Task) Start(ctx context.Context) error {
t.mu.Lock()
hasCgroup := t.cg != nil
t.mu.Unlock()
r, err := t.shim.Start(ctx, &shim.StartRequest{
ID: t.id,
})
if err != nil {
return errdefs.FromGRPC(err)
}
t.pid = int(r.Pid)
if !hasCgroup {
cg, err := cgroups.Load(cgroups.PidPath(t.pid))
if err != nil && err != cgroups.ErrCgroupDeleted {
return err
}
t.mu.Lock()
if err == cgroups.ErrCgroupDeleted {
t.cg = nil
} else {
t.cg = cg
}
t.mu.Unlock()
}
t.events.Publish(ctx, runtime.TaskStartEventTopic, &eventstypes.TaskStart{
ContainerID: t.id,
Pid: uint32(t.pid),
})
return nil
}
// State returns runtime information for the task
func (t *Task) State(ctx context.Context) (runtime.State, error) {
response, err := t.shim.State(ctx, &shim.StateRequest{
ID: t.id,
})
if err != nil {
if !errors.Is(err, ttrpc.ErrClosed) {
return runtime.State{}, errdefs.FromGRPC(err)
}
return runtime.State{}, errdefs.ErrNotFound
}
return runtime.State{
Pid: response.Pid,
Status: statusFromProto(response.Status),
Stdin: response.Stdin,
Stdout: response.Stdout,
Stderr: response.Stderr,
Terminal: response.Terminal,
ExitStatus: response.ExitStatus,
ExitedAt: protobuf.FromTimestamp(response.ExitedAt),
}, nil
}
// Pause the task and all processes
func (t *Task) Pause(ctx context.Context) error {
if _, err := t.shim.Pause(ctx, empty); err != nil {
return errdefs.FromGRPC(err)
}
t.events.Publish(ctx, runtime.TaskPausedEventTopic, &eventstypes.TaskPaused{
ContainerID: t.id,
})
return nil
}
// Resume the task and all processes
func (t *Task) Resume(ctx context.Context) error {
if _, err := t.shim.Resume(ctx, empty); err != nil {
return errdefs.FromGRPC(err)
}
t.events.Publish(ctx, runtime.TaskResumedEventTopic, &eventstypes.TaskResumed{
ContainerID: t.id,
})
return nil
}
// Kill the task using the provided signal
//
// Optionally send the signal to all processes that are a child of the task
func (t *Task) Kill(ctx context.Context, signal uint32, all bool) error {
if _, err := t.shim.Kill(ctx, &shim.KillRequest{
ID: t.id,
Signal: signal,
All: all,
}); err != nil {
return errdefs.FromGRPC(err)
}
return nil
}
// Exec creates a new process inside the task
func (t *Task) Exec(ctx context.Context, id string, opts runtime.ExecOpts) (runtime.ExecProcess, error) {
if err := identifiers.Validate(id); err != nil {
return nil, fmt.Errorf("invalid exec id: %w", err)
}
request := &shim.ExecProcessRequest{
ID: id,
Stdin: opts.IO.Stdin,
Stdout: opts.IO.Stdout,
Stderr: opts.IO.Stderr,
Terminal: opts.IO.Terminal,
Spec: opts.Spec,
}
if _, err := t.shim.Exec(ctx, request); err != nil {
return nil, errdefs.FromGRPC(err)
}
return &Process{
id: id,
t: t,
}, nil
}
// Pids returns all system level process ids running inside the task
func (t *Task) Pids(ctx context.Context) ([]runtime.ProcessInfo, error) {
resp, err := t.shim.ListPids(ctx, &shim.ListPidsRequest{
ID: t.id,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
var processList []runtime.ProcessInfo
for _, p := range resp.Processes {
processList = append(processList, runtime.ProcessInfo{
Pid: p.Pid,
Info: p.Info,
})
}
return processList, nil
}
// ResizePty changes the side of the task's PTY to the provided width and height
func (t *Task) ResizePty(ctx context.Context, size runtime.ConsoleSize) error {
_, err := t.shim.ResizePty(ctx, &shim.ResizePtyRequest{
ID: t.id,
Width: size.Width,
Height: size.Height,
})
if err != nil {
err = errdefs.FromGRPC(err)
}
return err
}
// CloseIO closes the provided IO on the task
func (t *Task) CloseIO(ctx context.Context) error {
_, err := t.shim.CloseIO(ctx, &shim.CloseIORequest{
ID: t.id,
Stdin: true,
})
if err != nil {
err = errdefs.FromGRPC(err)
}
return err
}
// Checkpoint creates a system level dump of the task and process information that can be later restored
func (t *Task) Checkpoint(ctx context.Context, path string, options *types.Any) error {
r := &shim.CheckpointTaskRequest{
Path: path,
Options: options,
}
if _, err := t.shim.Checkpoint(ctx, r); err != nil {
return errdefs.FromGRPC(err)
}
t.events.Publish(ctx, runtime.TaskCheckpointedEventTopic, &eventstypes.TaskCheckpointed{
ContainerID: t.id,
})
return nil
}
// Update changes runtime information of a running task
func (t *Task) Update(ctx context.Context, resources *types.Any, _ map[string]string) error {
if _, err := t.shim.Update(ctx, &shim.UpdateTaskRequest{
Resources: resources,
}); err != nil {
return errdefs.FromGRPC(err)
}
return nil
}
// Process returns a specific process inside the task by the process id
func (t *Task) Process(ctx context.Context, id string) (runtime.ExecProcess, error) {
p := &Process{
id: id,
t: t,
}
if _, err := p.State(ctx); err != nil {
return nil, err
}
return p, nil
}
// Stats returns runtime specific system level metric information for the task
func (t *Task) Stats(ctx context.Context) (*types.Any, error) {
t.mu.Lock()
defer t.mu.Unlock()
if t.cg == nil {
return nil, fmt.Errorf("cgroup does not exist: %w", errdefs.ErrNotFound)
}
stats, err := t.cg.Stat(cgroups.IgnoreNotExist)
if err != nil {
return nil, err
}
return protobuf.MarshalAnyToProto(stats)
}
// Cgroup returns the underlying cgroup for a linux task
func (t *Task) Cgroup() (cgroups.Cgroup, error) {
t.mu.Lock()
defer t.mu.Unlock()
if t.cg == nil {
return nil, fmt.Errorf("cgroup does not exist: %w", errdefs.ErrNotFound)
}
return t.cg, nil
}
// Wait for the task to exit returning the status and timestamp
func (t *Task) Wait(ctx context.Context) (*runtime.Exit, error) {
r, err := t.shim.Wait(ctx, &shim.WaitRequest{
ID: t.id,
})
if err != nil {
return nil, err
}
return &runtime.Exit{
Timestamp: protobuf.FromTimestamp(r.ExitedAt),
Status: r.ExitStatus,
}, nil
}

View File

@ -1,38 +0,0 @@
//go: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 v1
import (
"context"
"io"
"path/filepath"
"github.com/containerd/fifo"
"golang.org/x/sys/unix"
)
// OpenShimStdoutLog opens the shim log for reading
func OpenShimStdoutLog(ctx context.Context, logDirPath string) (io.ReadWriteCloser, error) {
return fifo.OpenFifo(ctx, filepath.Join(logDirPath, "shim.stdout.log"), unix.O_RDWR|unix.O_CREAT, 0700)
}
// OpenShimStderrLog opens the shim log
func OpenShimStderrLog(ctx context.Context, logDirPath string) (io.ReadWriteCloser, error) {
return fifo.OpenFifo(ctx, filepath.Join(logDirPath, "shim.stderr.log"), unix.O_RDWR|unix.O_CREAT, 0700)
}

View File

@ -1,430 +0,0 @@
//go: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 client
import (
"context"
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/log"
ptypes "github.com/containerd/containerd/protobuf/types"
v1 "github.com/containerd/containerd/runtime/v1"
"github.com/containerd/containerd/runtime/v1/shim"
shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/containerd/sys"
"github.com/containerd/ttrpc"
exec "golang.org/x/sys/execabs"
"golang.org/x/sys/unix"
)
var empty = &ptypes.Empty{}
// Opt is an option for a shim client configuration
type Opt func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error)
// WithStart executes a new shim process
func WithStart(binary, address, daemonAddress, cgroup string, debug bool, exitHandler func()) Opt {
return func(ctx context.Context, config shim.Config) (_ shimapi.ShimService, _ io.Closer, err error) {
socket, err := newSocket(address)
if err != nil {
if !eaddrinuse(err) {
return nil, nil, err
}
if err := RemoveSocket(address); err != nil {
return nil, nil, fmt.Errorf("remove already used socket: %w", err)
}
if socket, err = newSocket(address); err != nil {
return nil, nil, err
}
}
f, err := socket.File()
if err != nil {
return nil, nil, fmt.Errorf("failed to get fd for socket %s: %w", address, err)
}
defer f.Close()
stdoutCopy := io.Discard
stderrCopy := io.Discard
stdoutLog, err := v1.OpenShimStdoutLog(ctx, config.WorkDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to create stdout log: %w", err)
}
stderrLog, err := v1.OpenShimStderrLog(ctx, config.WorkDir)
if err != nil {
return nil, nil, fmt.Errorf("failed to create stderr log: %w", err)
}
if debug {
stdoutCopy = os.Stdout
stderrCopy = os.Stderr
}
go io.Copy(stdoutCopy, stdoutLog)
go io.Copy(stderrCopy, stderrLog)
cmd, err := newCommand(binary, daemonAddress, debug, config, f, stdoutLog, stderrLog)
if err != nil {
return nil, nil, err
}
if err := cmd.Start(); err != nil {
return nil, nil, fmt.Errorf("failed to start shim: %w", err)
}
defer func() {
if err != nil {
cmd.Process.Kill()
}
}()
go func() {
cmd.Wait()
exitHandler()
if stdoutLog != nil {
stdoutLog.Close()
}
if stderrLog != nil {
stderrLog.Close()
}
socket.Close()
RemoveSocket(address)
}()
log.G(ctx).WithFields(log.Fields{
"pid": cmd.Process.Pid,
"address": address,
"debug": debug,
}).Infof("shim %s started", binary)
if err := writeFile(filepath.Join(config.Path, "address"), address); err != nil {
return nil, nil, err
}
if err := writeFile(filepath.Join(config.Path, "shim.pid"), strconv.Itoa(cmd.Process.Pid)); err != nil {
return nil, nil, err
}
// set shim in cgroup if it is provided
if cgroup != "" {
if err := setCgroup(cgroup, cmd); err != nil {
return nil, nil, err
}
log.G(ctx).WithFields(log.Fields{
"pid": cmd.Process.Pid,
"address": address,
}).Infof("shim placed in cgroup %s", cgroup)
}
if err = setupOOMScore(cmd.Process.Pid); err != nil {
return nil, nil, err
}
c, clo, err := WithConnect(address, func() {})(ctx, config)
if err != nil {
return nil, nil, fmt.Errorf("failed to connect: %w", err)
}
return c, clo, nil
}
}
func eaddrinuse(err error) bool {
cause := errors.Unwrap(err)
netErr, ok := cause.(*net.OpError)
if !ok {
return false
}
if netErr.Op != "listen" {
return false
}
syscallErr, ok := netErr.Err.(*os.SyscallError)
if !ok {
return false
}
errno, ok := syscallErr.Err.(syscall.Errno)
if !ok {
return false
}
return errno == syscall.EADDRINUSE
}
// setupOOMScore gets containerd's oom score and adds +1 to it
// to ensure a shim has a lower* score than the daemons
// if not already at the maximum OOM Score
func setupOOMScore(shimPid int) error {
pid := os.Getpid()
score, err := sys.GetOOMScoreAdj(pid)
if err != nil {
return fmt.Errorf("get daemon OOM score: %w", err)
}
shimScore := score + 1
if err := sys.AdjustOOMScore(shimPid, shimScore); err != nil {
return fmt.Errorf("set shim OOM score: %w", err)
}
return nil
}
func newCommand(binary, daemonAddress string, debug bool, config shim.Config, socket *os.File, stdout, stderr io.Writer) (*exec.Cmd, error) {
selfExe, err := os.Executable()
if err != nil {
return nil, err
}
args := []string{
"-namespace", config.Namespace,
"-workdir", config.WorkDir,
"-address", daemonAddress,
"-containerd-binary", selfExe,
}
if config.RuntimeRoot != "" {
args = append(args, "-runtime-root", config.RuntimeRoot)
}
if config.SystemdCgroup {
args = append(args, "-systemd-cgroup")
}
if debug {
args = append(args, "-debug")
}
cmd := exec.Command(binary, args...)
cmd.Dir = config.Path
// make sure the shim can be re-parented to system init
// and is cloned in a new mount namespace because the overlay/filesystems
// will be mounted by the shim
cmd.SysProcAttr = getSysProcAttr()
cmd.ExtraFiles = append(cmd.ExtraFiles, socket)
cmd.Env = append(os.Environ(), "GOMAXPROCS=2")
cmd.Stdout = stdout
cmd.Stderr = stderr
return cmd, nil
}
// writeFile writes a address file atomically
func writeFile(path, address string) error {
path, err := filepath.Abs(path)
if err != nil {
return err
}
tempPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
f, err := os.OpenFile(tempPath, os.O_RDWR|os.O_CREATE|os.O_EXCL|os.O_SYNC, 0666)
if err != nil {
return err
}
_, err = f.WriteString(address)
f.Close()
if err != nil {
return err
}
return os.Rename(tempPath, path)
}
const (
abstractSocketPrefix = "\x00"
socketPathLimit = 106
)
type socket string
func (s socket) isAbstract() bool {
return !strings.HasPrefix(string(s), "unix://")
}
func (s socket) path() string {
path := strings.TrimPrefix(string(s), "unix://")
// if there was no trim performed, we assume an abstract socket
if len(path) == len(s) {
path = abstractSocketPrefix + path
}
return path
}
func newSocket(address string) (*net.UnixListener, error) {
if len(address) > socketPathLimit {
return nil, fmt.Errorf("%q: unix socket path too long (> %d)", address, socketPathLimit)
}
var (
sock = socket(address)
path = sock.path()
)
if !sock.isAbstract() {
if err := os.MkdirAll(filepath.Dir(path), 0600); err != nil {
return nil, fmt.Errorf("%s: %w", path, err)
}
}
l, err := net.Listen("unix", path)
if err != nil {
return nil, fmt.Errorf("failed to listen to unix socket %q (abstract: %t): %w", address, sock.isAbstract(), err)
}
if err := os.Chmod(path, 0600); err != nil {
l.Close()
return nil, err
}
return l.(*net.UnixListener), nil
}
// RemoveSocket removes the socket at the specified address if
// it exists on the filesystem
func RemoveSocket(address string) error {
sock := socket(address)
if !sock.isAbstract() {
return os.Remove(sock.path())
}
return nil
}
// AnonDialer returns a dialer for a socket
//
// NOTE: It is only used for testing.
func AnonDialer(address string, timeout time.Duration) (net.Conn, error) {
return anonDialer(address, timeout)
}
func connect(address string, d func(string, time.Duration) (net.Conn, error)) (net.Conn, error) {
return d(address, 100*time.Second)
}
func anonDialer(address string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout("unix", socket(address).path(), timeout)
}
// WithConnect connects to an existing shim
func WithConnect(address string, onClose func()) Opt {
return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) {
conn, err := connect(address, anonDialer)
if err != nil {
return nil, nil, err
}
client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onClose))
return shimapi.NewShimClient(client), conn, nil
}
}
// WithLocal uses an in process shim
func WithLocal(publisher events.Publisher) func(context.Context, shim.Config) (shimapi.ShimService, io.Closer, error) {
return func(ctx context.Context, config shim.Config) (shimapi.ShimService, io.Closer, error) {
service, err := shim.NewService(config, publisher)
if err != nil {
return nil, nil, err
}
return shim.NewLocal(service), nil, nil
}
}
// New returns a new shim client
func New(ctx context.Context, config shim.Config, opt Opt) (*Client, error) {
s, c, err := opt(ctx, config)
if err != nil {
return nil, err
}
return &Client{
ShimService: s,
c: c,
exitCh: make(chan struct{}),
}, nil
}
// Client is a shim client containing the connection to a shim
type Client struct {
shimapi.ShimService
c io.Closer
exitCh chan struct{}
exitOnce sync.Once
}
// IsAlive returns true if the shim can be contacted.
// NOTE: a negative answer doesn't mean that the process is gone.
func (c *Client) IsAlive(ctx context.Context) (bool, error) {
_, err := c.ShimInfo(ctx, empty)
if err != nil {
// TODO(stevvooe): There are some error conditions that need to be
// handle with unix sockets existence to give the right answer here.
return false, err
}
return true, nil
}
// StopShim signals the shim to exit and wait for the process to disappear
func (c *Client) StopShim(ctx context.Context) error {
return c.signalShim(ctx, unix.SIGTERM)
}
// KillShim kills the shim forcefully and wait for the process to disappear
func (c *Client) KillShim(ctx context.Context) error {
return c.signalShim(ctx, unix.SIGKILL)
}
// Close the client connection
func (c *Client) Close() error {
if c.c == nil {
return nil
}
return c.c.Close()
}
func (c *Client) signalShim(ctx context.Context, sig syscall.Signal) error {
info, err := c.ShimInfo(ctx, empty)
if err != nil {
return err
}
pid := int(info.ShimPid)
// make sure we don't kill ourselves if we are running a local shim
if os.Getpid() == pid {
return nil
}
if err := unix.Kill(pid, sig); err != nil && err != unix.ESRCH {
return err
}
// wait for shim to die after being signaled
select {
case <-ctx.Done():
return ctx.Err()
case <-c.waitForExit(ctx, pid):
return nil
}
}
func (c *Client) waitForExit(ctx context.Context, pid int) <-chan struct{} {
go c.exitOnce.Do(func() {
defer close(c.exitCh)
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
for {
// use kill(pid, 0) here because the shim could have been reparented
// and we are no longer able to waitpid(pid, ...) on the shim
if err := unix.Kill(pid, 0); err == unix.ESRCH {
return
}
select {
case <-ticker.C:
case <-ctx.Done():
log.G(ctx).WithField("pid", pid).Warn("timed out while waiting for shim to exit")
return
}
}
})
return c.exitCh
}

View File

@ -1,42 +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 client
import (
"fmt"
"syscall"
"github.com/containerd/cgroups/v3/cgroup1"
exec "golang.org/x/sys/execabs"
)
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
Setpgid: true,
}
}
func setCgroup(cgroupPath string, cmd *exec.Cmd) error {
cg, err := cgroup1.Load(cgroup1.StaticPath(cgroupPath))
if err != nil {
return fmt.Errorf("failed to load cgroup %s: %w", cgroupPath, err)
}
if err := cg.AddProc(uint64(cmd.Process.Pid)); err != nil {
return fmt.Errorf("failed to join cgroup %s: %w", cgroupPath, err)
}
return nil
}

View File

@ -1,35 +0,0 @@
//go:build !linux && !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 client
import (
"syscall"
exec "golang.org/x/sys/execabs"
)
func getSysProcAttr() *syscall.SysProcAttr {
return &syscall.SysProcAttr{
Setpgid: true,
}
}
func setCgroup(cgroupPath string, cmd *exec.Cmd) error {
return nil
}

View File

@ -1,107 +0,0 @@
//go: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 shim
import (
"context"
"path/filepath"
"github.com/containerd/containerd/mount"
ptypes "github.com/containerd/containerd/protobuf/types"
shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
)
// NewLocal returns a shim client implementation for issue commands to a shim
func NewLocal(s *Service) shimapi.ShimService {
return &local{
s: s,
}
}
type local struct {
s *Service
}
func (c *local) Create(ctx context.Context, in *shimapi.CreateTaskRequest) (*shimapi.CreateTaskResponse, error) {
return c.s.Create(ctx, in)
}
func (c *local) Start(ctx context.Context, in *shimapi.StartRequest) (*shimapi.StartResponse, error) {
return c.s.Start(ctx, in)
}
func (c *local) Delete(ctx context.Context, in *ptypes.Empty) (*shimapi.DeleteResponse, error) {
// make sure we unmount the containers rootfs for this local
if err := mount.Unmount(filepath.Join(c.s.config.Path, "rootfs"), 0); err != nil {
return nil, err
}
return c.s.Delete(ctx, in)
}
func (c *local) DeleteProcess(ctx context.Context, in *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) {
return c.s.DeleteProcess(ctx, in)
}
func (c *local) Exec(ctx context.Context, in *shimapi.ExecProcessRequest) (*ptypes.Empty, error) {
return c.s.Exec(ctx, in)
}
func (c *local) ResizePty(ctx context.Context, in *shimapi.ResizePtyRequest) (*ptypes.Empty, error) {
return c.s.ResizePty(ctx, in)
}
func (c *local) State(ctx context.Context, in *shimapi.StateRequest) (*shimapi.StateResponse, error) {
return c.s.State(ctx, in)
}
func (c *local) Pause(ctx context.Context, in *ptypes.Empty) (*ptypes.Empty, error) {
return c.s.Pause(ctx, in)
}
func (c *local) Resume(ctx context.Context, in *ptypes.Empty) (*ptypes.Empty, error) {
return c.s.Resume(ctx, in)
}
func (c *local) Kill(ctx context.Context, in *shimapi.KillRequest) (*ptypes.Empty, error) {
return c.s.Kill(ctx, in)
}
func (c *local) ListPids(ctx context.Context, in *shimapi.ListPidsRequest) (*shimapi.ListPidsResponse, error) {
return c.s.ListPids(ctx, in)
}
func (c *local) CloseIO(ctx context.Context, in *shimapi.CloseIORequest) (*ptypes.Empty, error) {
return c.s.CloseIO(ctx, in)
}
func (c *local) Checkpoint(ctx context.Context, in *shimapi.CheckpointTaskRequest) (*ptypes.Empty, error) {
return c.s.Checkpoint(ctx, in)
}
func (c *local) ShimInfo(ctx context.Context, in *ptypes.Empty) (*shimapi.ShimInfoResponse, error) {
return c.s.ShimInfo(ctx, in)
}
func (c *local) Update(ctx context.Context, in *shimapi.UpdateTaskRequest) (*ptypes.Empty, error) {
return c.s.Update(ctx, in)
}
func (c *local) Wait(ctx context.Context, in *shimapi.WaitRequest) (*shimapi.WaitResponse, error) {
return c.s.Wait(ctx, in)
}

View File

@ -1,679 +0,0 @@
//go: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 shim
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/containerd/console"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/api/types/task"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/process"
"github.com/containerd/containerd/pkg/stdio"
"github.com/containerd/containerd/protobuf"
ptypes "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/runctypes"
shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/containerd/sys/reaper"
runc "github.com/containerd/go-runc"
"github.com/containerd/typeurl/v2"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var (
empty = &ptypes.Empty{}
bufPool = sync.Pool{
New: func() interface{} {
buffer := make([]byte, 4096)
return &buffer
},
}
)
// Config contains shim specific configuration
type Config struct {
Path string
Namespace string
WorkDir string
// Criu is 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
RuntimeRoot string
SystemdCgroup bool
}
// NewService returns a new shim service that can be used via GRPC
func NewService(config Config, publisher events.Publisher) (*Service, error) {
if config.Namespace == "" {
return nil, fmt.Errorf("shim namespace cannot be empty")
}
ctx := namespaces.WithNamespace(context.Background(), config.Namespace)
ctx = log.WithLogger(ctx, logrus.WithFields(log.Fields{
"namespace": config.Namespace,
"path": config.Path,
"pid": os.Getpid(),
}))
s := &Service{
config: config,
context: ctx,
processes: make(map[string]process.Process),
events: make(chan interface{}, 128),
ec: reaper.Default.Subscribe(),
}
go s.processExits()
if err := s.initPlatform(); err != nil {
return nil, fmt.Errorf("failed to initialized platform behavior: %w", err)
}
go s.forward(publisher)
return s, nil
}
// Service is the shim implementation of a remote shim over GRPC
type Service struct {
mu sync.Mutex
config Config
context context.Context
processes map[string]process.Process
events chan interface{}
platform stdio.Platform
ec chan runc.Exit
// Filled by Create()
id string
bundle string
}
// Create a new initial process and container with the underlying OCI runtime
func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ *shimapi.CreateTaskResponse, err error) {
var pmounts []process.Mount
for _, m := range r.Rootfs {
pmounts = append(pmounts, process.Mount{
Type: m.Type,
Source: m.Source,
Target: m.Target,
Options: m.Options,
})
}
rootfs := ""
if len(pmounts) > 0 {
rootfs = filepath.Join(r.Bundle, "rootfs")
if err := os.Mkdir(rootfs, 0711); err != nil && !os.IsExist(err) {
return nil, err
}
}
config := &process.CreateConfig{
ID: r.ID,
Bundle: r.Bundle,
Runtime: r.Runtime,
Rootfs: pmounts,
Terminal: r.Terminal,
Stdin: r.Stdin,
Stdout: r.Stdout,
Stderr: r.Stderr,
Checkpoint: r.Checkpoint,
ParentCheckpoint: r.ParentCheckpoint,
Options: r.Options,
}
var mounts []mount.Mount
for _, pm := range pmounts {
mounts = append(mounts, mount.Mount{
Type: pm.Type,
Source: pm.Source,
Target: pm.Target,
Options: pm.Options,
})
}
defer func() {
if err != nil {
if err2 := mount.UnmountMounts(mounts, rootfs, 0); err2 != nil {
log.G(ctx).WithError(err2).Warn("Failed to cleanup rootfs mount")
}
}
}()
if err := mount.All(mounts, rootfs); err != nil {
return nil, fmt.Errorf("failed to mount rootfs component: %w", err)
}
s.mu.Lock()
defer s.mu.Unlock()
process, err := newInit(
ctx,
s.config.Path,
s.config.WorkDir,
s.config.RuntimeRoot,
s.config.Namespace,
s.config.SystemdCgroup,
s.platform,
config,
rootfs,
)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
if err := process.Create(ctx, config); err != nil {
return nil, errdefs.ToGRPC(err)
}
// save the main task id and bundle to the shim for additional requests
s.id = r.ID
s.bundle = r.Bundle
pid := process.Pid()
s.processes[r.ID] = process
return &shimapi.CreateTaskResponse{
Pid: uint32(pid),
}, nil
}
// Start a process
func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.StartResponse, error) {
p, err := s.getExecProcess(r.ID)
if err != nil {
return nil, err
}
if err := p.Start(ctx); err != nil {
return nil, err
}
return &shimapi.StartResponse{
ID: p.ID(),
Pid: uint32(p.Pid()),
}, nil
}
// Delete the initial process and container
func (s *Service) Delete(ctx context.Context, r *ptypes.Empty) (*shimapi.DeleteResponse, error) {
p, err := s.getInitProcess()
if err != nil {
return nil, err
}
if err := p.Delete(ctx); err != nil {
return nil, errdefs.ToGRPC(err)
}
s.mu.Lock()
delete(s.processes, s.id)
s.mu.Unlock()
s.platform.Close()
return &shimapi.DeleteResponse{
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: protobuf.ToTimestamp(p.ExitedAt()),
Pid: uint32(p.Pid()),
}, nil
}
// DeleteProcess deletes an exec'd process
func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) {
if r.ID == s.id {
return nil, status.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess")
}
p, err := s.getExecProcess(r.ID)
if err != nil {
return nil, err
}
if err := p.Delete(ctx); err != nil {
return nil, errdefs.ToGRPC(err)
}
s.mu.Lock()
delete(s.processes, r.ID)
s.mu.Unlock()
return &shimapi.DeleteResponse{
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: protobuf.ToTimestamp(p.ExitedAt()),
Pid: uint32(p.Pid()),
}, nil
}
// Exec an additional process inside the container
func (s *Service) Exec(ctx context.Context, r *shimapi.ExecProcessRequest) (*ptypes.Empty, error) {
s.mu.Lock()
if p := s.processes[r.ID]; p != nil {
s.mu.Unlock()
return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "id %s", r.ID)
}
p := s.processes[s.id]
s.mu.Unlock()
if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
process, err := p.(*process.Init).Exec(ctx, s.config.Path, &process.ExecConfig{
ID: r.ID,
Terminal: r.Terminal,
Stdin: r.Stdin,
Stdout: r.Stdout,
Stderr: r.Stderr,
Spec: r.Spec,
})
if err != nil {
return nil, errdefs.ToGRPC(err)
}
s.mu.Lock()
s.processes[r.ID] = process
s.mu.Unlock()
return empty, nil
}
// ResizePty of a process
func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*ptypes.Empty, error) {
if r.ID == "" {
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "id not provided")
}
ws := console.WinSize{
Width: uint16(r.Width),
Height: uint16(r.Height),
}
s.mu.Lock()
p := s.processes[r.ID]
s.mu.Unlock()
if p == nil {
return nil, fmt.Errorf("process does not exist %s", r.ID)
}
if err := p.Resize(ws); 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 *shimapi.StateRequest) (*shimapi.StateResponse, error) {
p, err := s.getExecProcess(r.ID)
if err != nil {
return nil, err
}
st, err := p.Status(ctx)
if err != nil {
return nil, err
}
status := task.Status_UNKNOWN
switch st {
case "created":
status = task.Status_CREATED
case "running":
status = task.Status_RUNNING
case "stopped":
status = task.Status_STOPPED
case "paused":
status = task.Status_PAUSED
case "pausing":
status = task.Status_PAUSING
}
sio := p.Stdio()
return &shimapi.StateResponse{
ID: p.ID(),
Bundle: s.bundle,
Pid: uint32(p.Pid()),
Status: status,
Stdin: sio.Stdin,
Stdout: sio.Stdout,
Stderr: sio.Stderr,
Terminal: sio.Terminal,
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: protobuf.ToTimestamp(p.ExitedAt()),
}, nil
}
// Pause the container
func (s *Service) Pause(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, error) {
p, err := s.getInitProcess()
if err != nil {
return nil, err
}
if err := p.(*process.Init).Pause(ctx); err != nil {
return nil, err
}
return empty, nil
}
// Resume the container
func (s *Service) Resume(ctx context.Context, r *ptypes.Empty) (*ptypes.Empty, error) {
p, err := s.getInitProcess()
if err != nil {
return nil, err
}
if err := p.(*process.Init).Resume(ctx); err != nil {
return nil, err
}
return empty, nil
}
// Kill a process with the provided signal
func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*ptypes.Empty, error) {
if r.ID == "" {
p, err := s.getInitProcess()
if err != nil {
return nil, err
}
if err := p.Kill(ctx, r.Signal, r.All); err != nil {
return nil, errdefs.ToGRPC(err)
}
return empty, nil
}
p, err := s.getExecProcess(r.ID)
if err != nil {
return nil, err
}
if err := p.Kill(ctx, r.Signal, r.All); err != nil {
return nil, errdefs.ToGRPC(err)
}
return empty, nil
}
// ListPids returns all pids inside the container
func (s *Service) ListPids(ctx context.Context, r *shimapi.ListPidsRequest) (*shimapi.ListPidsResponse, error) {
pids, err := s.getContainerPids(ctx, r.ID)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
var processes []*task.ProcessInfo
s.mu.Lock()
defer s.mu.Unlock()
for _, pid := range pids {
pInfo := task.ProcessInfo{
Pid: pid,
}
for _, p := range s.processes {
if p.Pid() == int(pid) {
d := &runctypes.ProcessDetails{
ExecID: p.ID(),
}
a, err := typeurl.MarshalAny(d)
if err != nil {
return nil, fmt.Errorf("failed to marshal process %d info: %w", pid, err)
}
pInfo.Info = protobuf.FromAny(a)
break
}
}
processes = append(processes, &pInfo)
}
return &shimapi.ListPidsResponse{
Processes: processes,
}, nil
}
// CloseIO of a process
func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*ptypes.Empty, error) {
p, err := s.getExecProcess(r.ID)
if err != nil {
return nil, err
}
if stdin := p.Stdin(); stdin != nil {
if err := stdin.Close(); err != nil {
return nil, fmt.Errorf("close stdin: %w", err)
}
}
return empty, nil
}
// Checkpoint the container
func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskRequest) (*ptypes.Empty, error) {
p, err := s.getInitProcess()
if err != nil {
return nil, err
}
var options *runctypes.CheckpointOptions
if r.Options != nil {
v, err := typeurl.UnmarshalAny(r.Options)
if err != nil {
return nil, err
}
options = v.(*runctypes.CheckpointOptions)
}
if err := p.(*process.Init).Checkpoint(ctx, &process.CheckpointConfig{
Path: r.Path,
Exit: options.Exit,
AllowOpenTCP: options.OpenTcp,
AllowExternalUnixSockets: options.ExternalUnixSockets,
AllowTerminal: options.Terminal,
FileLocks: options.FileLocks,
EmptyNamespaces: options.EmptyNamespaces,
WorkDir: options.WorkPath,
}); err != nil {
return nil, errdefs.ToGRPC(err)
}
return empty, nil
}
// ShimInfo returns shim information such as the shim's pid
func (s *Service) ShimInfo(ctx context.Context, r *ptypes.Empty) (*shimapi.ShimInfoResponse, error) {
return &shimapi.ShimInfoResponse{
ShimPid: uint32(os.Getpid()),
}, nil
}
// Update a running container
func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*ptypes.Empty, error) {
p, err := s.getInitProcess()
if err != nil {
return nil, err
}
if err := p.(*process.Init).Update(ctx, r.Resources); err != nil {
return nil, errdefs.ToGRPC(err)
}
return empty, nil
}
// Wait for a process to exit
func (s *Service) Wait(ctx context.Context, r *shimapi.WaitRequest) (*shimapi.WaitResponse, error) {
p, err := s.getExecProcess(r.ID)
if err != nil {
return nil, err
}
p.Wait()
return &shimapi.WaitResponse{
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: protobuf.ToTimestamp(p.ExitedAt()),
}, nil
}
func (s *Service) processExits() {
for e := range s.ec {
s.checkProcesses(e)
}
}
func (s *Service) checkProcesses(e runc.Exit) {
var p process.Process
s.mu.Lock()
for _, proc := range s.processes {
if proc.Pid() == e.Pid {
p = proc
break
}
}
s.mu.Unlock()
if p == nil {
log.G(s.context).Debugf("process with id:%d wasn't found", e.Pid)
return
}
if ip, ok := p.(*process.Init); ok {
// Ensure all children are killed
if shouldKillAllOnExit(s.context, s.bundle) {
if err := ip.KillAll(s.context); err != nil {
log.G(s.context).WithError(err).WithField("id", ip.ID()).
Error("failed to kill init's children")
}
}
}
p.SetExited(e.Status)
s.events <- &eventstypes.TaskExit{
ContainerID: s.id,
ID: p.ID(),
Pid: uint32(e.Pid),
ExitStatus: uint32(e.Status),
ExitedAt: protobuf.ToTimestamp(p.ExitedAt()),
}
}
func shouldKillAllOnExit(ctx context.Context, bundlePath string) bool {
var bundleSpec specs.Spec
bundleConfigContents, err := os.ReadFile(filepath.Join(bundlePath, "config.json"))
if err != nil {
log.G(ctx).WithError(err).Error("shouldKillAllOnExit: failed to read config.json")
return true
}
if err := json.Unmarshal(bundleConfigContents, &bundleSpec); err != nil {
log.G(ctx).WithError(err).Error("shouldKillAllOnExit: failed to unmarshal bundle json")
return true
}
if bundleSpec.Linux != nil {
for _, ns := range bundleSpec.Linux.Namespaces {
if ns.Type == specs.PIDNamespace && ns.Path == "" {
return false
}
}
}
return true
}
func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
p, err := s.getInitProcess()
if err != nil {
return nil, 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(publisher events.Publisher) {
for e := range s.events {
if err := publisher.Publish(s.context, getTopic(s.context, e), e); err != nil {
log.G(s.context).WithError(err).Error("post event")
}
}
}
// getInitProcess returns initial process
func (s *Service) getInitProcess() (process.Process, error) {
s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
return p, nil
}
// getExecProcess returns exec process
func (s *Service) getExecProcess(id string) (process.Process, error) {
s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[id]
if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s does not exist", id)
}
return p, nil
}
func getTopic(ctx context.Context, e interface{}) string {
switch e.(type) {
case *eventstypes.TaskCreate:
return runtime.TaskCreateEventTopic
case *eventstypes.TaskStart:
return runtime.TaskStartEventTopic
case *eventstypes.TaskOOM:
return runtime.TaskOOMEventTopic
case *eventstypes.TaskExit:
return runtime.TaskExitEventTopic
case *eventstypes.TaskDelete:
return runtime.TaskDeleteEventTopic
case *eventstypes.TaskExecAdded:
return runtime.TaskExecAddedEventTopic
case *eventstypes.TaskExecStarted:
return runtime.TaskExecStartedEventTopic
case *eventstypes.TaskPaused:
return runtime.TaskPausedEventTopic
case *eventstypes.TaskResumed:
return runtime.TaskResumedEventTopic
case *eventstypes.TaskCheckpointed:
return runtime.TaskCheckpointedEventTopic
default:
logrus.Warnf("no topic for type %#v", e)
}
return runtime.TaskUnknownTopic
}
func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace string, systemdCgroup bool, platform stdio.Platform, r *process.CreateConfig, rootfs string) (*process.Init, error) {
options := &runctypes.CreateOptions{}
if r.Options != nil {
v, err := typeurl.UnmarshalAny(r.Options)
if err != nil {
return nil, err
}
options = v.(*runctypes.CreateOptions)
}
runtime := process.NewRunc(runtimeRoot, path, namespace, r.Runtime, systemdCgroup)
p := process.New(r.ID, runtime, stdio.Stdio{
Stdin: r.Stdin,
Stdout: r.Stdout,
Stderr: r.Stderr,
Terminal: r.Terminal,
})
p.Bundle = r.Bundle
p.Platform = platform
p.Rootfs = rootfs
p.WorkDir = workDir
p.IoUID = int(options.IoUid)
p.IoGID = int(options.IoGid)
p.NoPivotRoot = options.NoPivotRoot
p.NoNewKeyring = options.NoNewKeyring
p.CriuWorkPath = options.CriuWorkPath
if p.CriuWorkPath == "" {
// if criu work path not set, use container WorkDir
p.CriuWorkPath = p.WorkDir
}
return p, nil
}

View File

@ -1,193 +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 shim
import (
"context"
"errors"
"fmt"
"io"
"net/url"
"os"
"sync"
"syscall"
"github.com/containerd/console"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/process"
"github.com/containerd/fifo"
)
type linuxPlatform struct {
epoller *console.Epoller
}
func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, id, stdin, stdout, stderr string, wg *sync.WaitGroup) (cons console.Console, retErr error) {
if p.epoller == nil {
return nil, errors.New("uninitialized epoller")
}
epollConsole, err := p.epoller.Add(console)
if err != nil {
return nil, err
}
var cwg sync.WaitGroup
if stdin != "" {
in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0)
if err != nil {
return nil, err
}
cwg.Add(1)
go func() {
cwg.Done()
bp := bufPool.Get().(*[]byte)
defer bufPool.Put(bp)
io.CopyBuffer(epollConsole, in, *bp)
// we need to shutdown epollConsole when pipe broken
epollConsole.Shutdown(p.epoller.CloseConsole)
epollConsole.Close()
in.Close()
}()
}
uri, err := url.Parse(stdout)
if err != nil {
return nil, fmt.Errorf("unable to parse stdout uri: %w", err)
}
switch uri.Scheme {
case "binary":
ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
cmd := process.NewBinaryCmd(uri, id, ns)
// In case of unexpected errors during logging binary start, close open pipes
var filesToClose []*os.File
defer func() {
if retErr != nil {
process.CloseFiles(filesToClose...)
}
}()
// Create pipe to be used by logging binary for Stdout
outR, outW, err := os.Pipe()
if err != nil {
return nil, fmt.Errorf("failed to create stdout pipes: %w", err)
}
filesToClose = append(filesToClose, outR)
// Stderr is created for logging binary but unused when terminal is true
serrR, _, err := os.Pipe()
if err != nil {
return nil, fmt.Errorf("failed to create stderr pipes: %w", err)
}
filesToClose = append(filesToClose, serrR)
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
filesToClose = append(filesToClose, r)
cmd.ExtraFiles = append(cmd.ExtraFiles, outR, serrR, w)
wg.Add(1)
cwg.Add(1)
go func() {
cwg.Done()
io.Copy(outW, epollConsole)
outW.Close()
wg.Done()
}()
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to start logging binary process: %w", err)
}
// Close our side of the pipe after start
if err := w.Close(); err != nil {
return nil, fmt.Errorf("failed to close write pipe after start: %w", err)
}
// Wait for the logging binary to be ready
b := make([]byte, 1)
if _, err := r.Read(b); err != nil && err != io.EOF {
return nil, fmt.Errorf("failed to read from logging binary: %w", err)
}
cwg.Wait()
default:
outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0)
if err != nil {
return nil, err
}
outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0)
if err != nil {
return nil, err
}
wg.Add(1)
cwg.Add(1)
go func() {
cwg.Done()
p := bufPool.Get().(*[]byte)
defer bufPool.Put(p)
io.CopyBuffer(outw, epollConsole, *p)
outw.Close()
outr.Close()
wg.Done()
}()
cwg.Wait()
}
return epollConsole, nil
}
func (p *linuxPlatform) ShutdownConsole(ctx context.Context, cons console.Console) error {
if p.epoller == nil {
return errors.New("uninitialized epoller")
}
epollConsole, ok := cons.(*console.EpollConsole)
if !ok {
return fmt.Errorf("expected EpollConsole, got %#v", cons)
}
return epollConsole.Shutdown(p.epoller.CloseConsole)
}
func (p *linuxPlatform) Close() error {
return p.epoller.Close()
}
// 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
}
epoller, err := console.NewEpoller()
if err != nil {
return fmt.Errorf("failed to initialize epoller: %w", err)
}
s.platform = &linuxPlatform{
epoller: epoller,
}
go epoller.Wait()
return nil
}

View File

@ -1,160 +0,0 @@
//go:build !windows && !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 shim
import (
"context"
"fmt"
"io"
"net/url"
"os"
"sync"
"syscall"
"github.com/containerd/console"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/process"
"github.com/containerd/fifo"
)
type unixPlatform struct {
}
func (p *unixPlatform) CopyConsole(ctx context.Context, console console.Console, id, stdin, stdout, stderr string, wg *sync.WaitGroup) (cons console.Console, retErr error) {
var cwg sync.WaitGroup
if stdin != "" {
in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0)
if err != nil {
return nil, err
}
cwg.Add(1)
go func() {
cwg.Done()
p := bufPool.Get().(*[]byte)
defer bufPool.Put(p)
io.CopyBuffer(console, in, *p)
}()
}
uri, err := url.Parse(stdout)
if err != nil {
return nil, fmt.Errorf("unable to parse stdout uri: %w", err)
}
switch uri.Scheme {
case "binary":
ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
cmd := process.NewBinaryCmd(uri, id, ns)
// In case of unexpected errors during logging binary start, close open pipes
var filesToClose []*os.File
defer func() {
if retErr != nil {
process.CloseFiles(filesToClose...)
}
}()
// Create pipe to be used by logging binary for Stdout
outR, outW, err := os.Pipe()
if err != nil {
return nil, fmt.Errorf("failed to create stdout pipes: %w", err)
}
filesToClose = append(filesToClose, outR)
// Stderr is created for logging binary but unused when terminal is true
serrR, _, err := os.Pipe()
if err != nil {
return nil, fmt.Errorf("failed to create stderr pipes: %w", err)
}
filesToClose = append(filesToClose, serrR)
r, w, err := os.Pipe()
if err != nil {
return nil, err
}
filesToClose = append(filesToClose, r)
cmd.ExtraFiles = append(cmd.ExtraFiles, outR, serrR, w)
wg.Add(1)
cwg.Add(1)
go func() {
cwg.Done()
io.Copy(outW, console)
outW.Close()
wg.Done()
}()
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to start logging binary process: %w", err)
}
// Close our side of the pipe after start
if err := w.Close(); err != nil {
return nil, fmt.Errorf("failed to close write pipe after start: %w", err)
}
// Wait for the logging binary to be ready
b := make([]byte, 1)
if _, err := r.Read(b); err != nil && err != io.EOF {
return nil, fmt.Errorf("failed to read from logging binary: %w", err)
}
cwg.Wait()
default:
outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0)
if err != nil {
return nil, err
}
outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0)
if err != nil {
return nil, err
}
wg.Add(1)
cwg.Add(1)
go func() {
cwg.Done()
p := bufPool.Get().(*[]byte)
defer bufPool.Put(p)
io.CopyBuffer(outw, console, *p)
outw.Close()
outr.Close()
wg.Done()
}()
cwg.Wait()
}
return console, nil
}
func (p *unixPlatform) ShutdownConsole(ctx context.Context, cons console.Console) error {
return nil
}
func (p *unixPlatform) Close() error {
return nil
}
func (s *Service) initPlatform() error {
s.platform = &unixPlatform{}
return nil
}

View File

@ -1,17 +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 shim

File diff suppressed because it is too large Load Diff

View File

@ -1,180 +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.
*/
syntax = "proto3";
package containerd.runtime.linux.shim.v1;
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "github.com/containerd/containerd/api/types/mount.proto";
import "github.com/containerd/containerd/api/types/task/task.proto";
option go_package = "github.com/containerd/containerd/runtime/v1/shim/v1;shim";
// Shim service is launched for each container and is responsible for owning the IO
// for the container and its additional processes. The shim is also the parent of
// each container and allows reattaching to the IO and receiving the exit status
// for the container processes.
service Shim {
// State returns shim and task state information.
rpc State(StateRequest) returns (StateResponse);
rpc Create(CreateTaskRequest) returns (CreateTaskResponse);
rpc Start(StartRequest) returns (StartResponse);
rpc Delete(google.protobuf.Empty) returns (DeleteResponse);
rpc DeleteProcess(DeleteProcessRequest) returns (DeleteResponse);
rpc ListPids(ListPidsRequest) returns (ListPidsResponse);
rpc Pause(google.protobuf.Empty) returns (google.protobuf.Empty);
rpc Resume(google.protobuf.Empty) returns (google.protobuf.Empty);
rpc Checkpoint(CheckpointTaskRequest) returns (google.protobuf.Empty);
rpc Kill(KillRequest) returns (google.protobuf.Empty);
rpc Exec(ExecProcessRequest) returns (google.protobuf.Empty);
rpc ResizePty(ResizePtyRequest) returns (google.protobuf.Empty);
rpc CloseIO(CloseIORequest) returns (google.protobuf.Empty);
// ShimInfo returns information about the shim.
rpc ShimInfo(google.protobuf.Empty) returns (ShimInfoResponse);
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
rpc Wait(WaitRequest) returns (WaitResponse);
}
message CreateTaskRequest {
string id = 1;
string bundle = 2;
string runtime = 3;
repeated containerd.types.Mount rootfs = 4;
bool terminal = 5;
string stdin = 6;
string stdout = 7;
string stderr = 8;
string checkpoint = 9;
string parent_checkpoint = 10;
google.protobuf.Any options = 11;
}
message CreateTaskResponse {
uint32 pid = 1;
}
message DeleteResponse {
uint32 pid = 1;
uint32 exit_status = 2;
google.protobuf.Timestamp exited_at = 3;
}
message DeleteProcessRequest {
string id = 1;
}
message ExecProcessRequest {
string id = 1;
bool terminal = 2;
string stdin = 3;
string stdout = 4;
string stderr = 5;
google.protobuf.Any spec = 6;
}
message ExecProcessResponse {
}
message ResizePtyRequest {
string id = 1;
uint32 width = 2;
uint32 height = 3;
}
message StateRequest {
string id = 1;
}
message StateResponse {
string id = 1;
string bundle = 2;
uint32 pid = 3;
containerd.v1.types.Status status = 4;
string stdin = 5;
string stdout = 6;
string stderr = 7;
bool terminal = 8;
uint32 exit_status = 9;
google.protobuf.Timestamp exited_at = 10;
}
message KillRequest {
string id = 1;
uint32 signal = 2;
bool all = 3;
}
message CloseIORequest {
string id = 1;
bool stdin = 2;
}
message ListPidsRequest {
string id = 1;
}
message ListPidsResponse {
repeated containerd.v1.types.ProcessInfo processes = 1;
}
message CheckpointTaskRequest {
string path = 1;
google.protobuf.Any options = 2;
}
message ShimInfoResponse {
uint32 shim_pid = 1;
}
message UpdateTaskRequest {
google.protobuf.Any resources = 1;
}
message StartRequest {
string id = 1;
}
message StartResponse {
string id = 1;
uint32 pid = 2;
}
message WaitRequest {
string id = 1;
}
message WaitResponse {
uint32 exit_status = 1;
google.protobuf.Timestamp exited_at = 2;
}

View File

@ -1,285 +0,0 @@
// Code generated by protoc-gen-go-ttrpc. DO NOT EDIT.
// source: github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto
package shim
import (
context "context"
ttrpc "github.com/containerd/ttrpc"
emptypb "google.golang.org/protobuf/types/known/emptypb"
)
type ShimService interface {
State(context.Context, *StateRequest) (*StateResponse, error)
Create(context.Context, *CreateTaskRequest) (*CreateTaskResponse, error)
Start(context.Context, *StartRequest) (*StartResponse, error)
Delete(context.Context, *emptypb.Empty) (*DeleteResponse, error)
DeleteProcess(context.Context, *DeleteProcessRequest) (*DeleteResponse, error)
ListPids(context.Context, *ListPidsRequest) (*ListPidsResponse, error)
Pause(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
Resume(context.Context, *emptypb.Empty) (*emptypb.Empty, error)
Checkpoint(context.Context, *CheckpointTaskRequest) (*emptypb.Empty, error)
Kill(context.Context, *KillRequest) (*emptypb.Empty, error)
Exec(context.Context, *ExecProcessRequest) (*emptypb.Empty, error)
ResizePty(context.Context, *ResizePtyRequest) (*emptypb.Empty, error)
CloseIO(context.Context, *CloseIORequest) (*emptypb.Empty, error)
ShimInfo(context.Context, *emptypb.Empty) (*ShimInfoResponse, error)
Update(context.Context, *UpdateTaskRequest) (*emptypb.Empty, error)
Wait(context.Context, *WaitRequest) (*WaitResponse, error)
}
func RegisterShimService(srv *ttrpc.Server, svc ShimService) {
srv.RegisterService("containerd.runtime.linux.shim.v1.Shim", &ttrpc.ServiceDesc{
Methods: map[string]ttrpc.Method{
"State": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req StateRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.State(ctx, &req)
},
"Create": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req CreateTaskRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Create(ctx, &req)
},
"Start": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req StartRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Start(ctx, &req)
},
"Delete": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req emptypb.Empty
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Delete(ctx, &req)
},
"DeleteProcess": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req DeleteProcessRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.DeleteProcess(ctx, &req)
},
"ListPids": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req ListPidsRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.ListPids(ctx, &req)
},
"Pause": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req emptypb.Empty
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Pause(ctx, &req)
},
"Resume": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req emptypb.Empty
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Resume(ctx, &req)
},
"Checkpoint": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req CheckpointTaskRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Checkpoint(ctx, &req)
},
"Kill": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req KillRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Kill(ctx, &req)
},
"Exec": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req ExecProcessRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Exec(ctx, &req)
},
"ResizePty": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req ResizePtyRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.ResizePty(ctx, &req)
},
"CloseIO": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req CloseIORequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.CloseIO(ctx, &req)
},
"ShimInfo": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req emptypb.Empty
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.ShimInfo(ctx, &req)
},
"Update": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req UpdateTaskRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Update(ctx, &req)
},
"Wait": func(ctx context.Context, unmarshal func(interface{}) error) (interface{}, error) {
var req WaitRequest
if err := unmarshal(&req); err != nil {
return nil, err
}
return svc.Wait(ctx, &req)
},
},
})
}
type shimClient struct {
client *ttrpc.Client
}
func NewShimClient(client *ttrpc.Client) ShimService {
return &shimClient{
client: client,
}
}
func (c *shimClient) State(ctx context.Context, req *StateRequest) (*StateResponse, error) {
var resp StateResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "State", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Create(ctx context.Context, req *CreateTaskRequest) (*CreateTaskResponse, error) {
var resp CreateTaskResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Create", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Start(ctx context.Context, req *StartRequest) (*StartResponse, error) {
var resp StartResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Start", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Delete(ctx context.Context, req *emptypb.Empty) (*DeleteResponse, error) {
var resp DeleteResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Delete", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) DeleteProcess(ctx context.Context, req *DeleteProcessRequest) (*DeleteResponse, error) {
var resp DeleteResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "DeleteProcess", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) ListPids(ctx context.Context, req *ListPidsRequest) (*ListPidsResponse, error) {
var resp ListPidsResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "ListPids", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Pause(ctx context.Context, req *emptypb.Empty) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Pause", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Resume(ctx context.Context, req *emptypb.Empty) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Resume", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Checkpoint(ctx context.Context, req *CheckpointTaskRequest) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Checkpoint", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Kill(ctx context.Context, req *KillRequest) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Kill", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Exec(ctx context.Context, req *ExecProcessRequest) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Exec", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) ResizePty(ctx context.Context, req *ResizePtyRequest) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "ResizePty", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) CloseIO(ctx context.Context, req *CloseIORequest) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "CloseIO", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) ShimInfo(ctx context.Context, req *emptypb.Empty) (*ShimInfoResponse, error) {
var resp ShimInfoResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "ShimInfo", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Update(ctx context.Context, req *UpdateTaskRequest) (*emptypb.Empty, error) {
var resp emptypb.Empty
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Update", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
func (c *shimClient) Wait(ctx context.Context, req *WaitRequest) (*WaitResponse, error) {
var resp WaitResponse
if err := c.client.Call(ctx, "containerd.runtime.linux.shim.v1.Shim", "Wait", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}

View File

@ -1,736 +0,0 @@
//go: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 v1
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
goruntime "runtime"
"sync"
"syscall"
"time"
"github.com/containerd/cgroups/v3/cgroup1"
eventstypes "github.com/containerd/containerd/api/events"
taskAPI "github.com/containerd/containerd/api/runtime/task/v2"
"github.com/containerd/containerd/api/types/task"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/oom"
oomv1 "github.com/containerd/containerd/pkg/oom/v1"
"github.com/containerd/containerd/pkg/process"
"github.com/containerd/containerd/pkg/schedcore"
"github.com/containerd/containerd/pkg/stdio"
"github.com/containerd/containerd/protobuf"
"github.com/containerd/containerd/protobuf/proto"
ptypes "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/runtime/v2/runc"
"github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/containerd/runtime/v2/shim"
"github.com/containerd/containerd/sys/reaper"
runcC "github.com/containerd/go-runc"
"github.com/containerd/typeurl/v2"
"github.com/sirupsen/logrus"
exec "golang.org/x/sys/execabs"
"golang.org/x/sys/unix"
)
var (
_ = (taskAPI.TaskService)(&service{})
empty = &ptypes.Empty{}
)
// New returns a new shim service that can be used via GRPC
func New(ctx context.Context, id string, publisher shim.Publisher, shutdown func()) (shim.Shim, error) {
ep, err := oomv1.New(publisher)
if err != nil {
return nil, err
}
go ep.Run(ctx)
s := &service{
id: id,
context: ctx,
events: make(chan interface{}, 128),
ec: reaper.Default.Subscribe(),
ep: ep,
cancel: shutdown,
}
go s.processExits()
runcC.Monitor = reaper.Default
if err := s.initPlatform(); err != nil {
shutdown()
return nil, fmt.Errorf("failed to initialized platform behavior: %w", err)
}
go s.forward(ctx, publisher)
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
id string
container *runc.Container
cancel func()
}
func newCommand(ctx context.Context, id, containerdAddress, containerdTTRPCAddress string) (*exec.Cmd, error) {
ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
self, err := os.Executable()
if err != nil {
return nil, err
}
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
args := []string{
"-namespace", ns,
"-id", id,
"-address", containerdAddress,
}
cmd := exec.Command(self, args...)
cmd.Dir = cwd
cmd.Env = append(os.Environ(), "GOMAXPROCS=2")
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
return cmd, nil
}
func (s *service) StartShim(ctx context.Context, opts shim.StartOpts) (_ string, retErr error) {
cmd, err := newCommand(ctx, opts.ID, opts.Address, opts.TTRPCAddress)
if err != nil {
return "", err
}
address, err := shim.SocketAddress(ctx, opts.Address, opts.ID)
if err != nil {
return "", err
}
socket, err := shim.NewSocket(address)
if err != nil {
if !shim.SocketEaddrinuse(err) {
return "", err
}
if err := shim.RemoveSocket(address); err != nil {
return "", fmt.Errorf("remove already used socket: %w", err)
}
if socket, err = shim.NewSocket(address); err != nil {
return "", err
}
}
defer func() {
if retErr != nil {
socket.Close()
_ = shim.RemoveSocket(address)
}
}()
// make sure that reexec shim-v2 binary use the value if need
if err := shim.WriteAddress("address", address); err != nil {
return "", err
}
f, err := socket.File()
if err != nil {
return "", err
}
cmd.ExtraFiles = append(cmd.ExtraFiles, f)
goruntime.LockOSThread()
if os.Getenv("SCHED_CORE") != "" {
if err := schedcore.Create(schedcore.ProcessGroup); err != nil {
return "", fmt.Errorf("enable sched core support: %w", err)
}
}
if err := cmd.Start(); err != nil {
f.Close()
return "", err
}
goruntime.UnlockOSThread()
defer func() {
if retErr != nil {
cmd.Process.Kill()
}
}()
// make sure to wait after start
go cmd.Wait()
if err := shim.WritePidFile("shim.pid", cmd.Process.Pid); err != nil {
return "", err
}
if data, err := io.ReadAll(os.Stdin); err == nil {
if len(data) > 0 {
var any ptypes.Any
if err := proto.Unmarshal(data, &any); err != nil {
return "", err
}
v, err := typeurl.UnmarshalAny(&any)
if err != nil {
return "", err
}
if opts, ok := v.(*options.Options); ok {
if opts.ShimCgroup != "" {
cg, err := cgroup1.Load(cgroup1.StaticPath(opts.ShimCgroup))
if err != nil {
return "", fmt.Errorf("failed to load cgroup %s: %w", opts.ShimCgroup, err)
}
if err := cg.AddProc(uint64(cmd.Process.Pid)); err != nil {
return "", fmt.Errorf("failed to join cgroup %s: %w", opts.ShimCgroup, err)
}
}
}
}
}
if err := shim.AdjustOOMScore(cmd.Process.Pid); err != nil {
return "", fmt.Errorf("failed to adjust OOM score for shim: %w", err)
}
return address, nil
}
func (s *service) Cleanup(ctx context.Context) (*taskAPI.DeleteResponse, error) {
if address, err := shim.ReadAddress("address"); err == nil {
if err = shim.RemoveSocket(address); err != nil {
return nil, err
}
}
path, err := os.Getwd()
if err != nil {
return nil, err
}
ns, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
runtime, err := runc.ReadRuntime(path)
if err != nil {
return nil, err
}
opts, err := runc.ReadOptions(path)
if err != nil {
return nil, err
}
root := process.RuncRoot
if opts != nil && opts.Root != "" {
root = opts.Root
}
r := process.NewRunc(root, path, ns, runtime, false)
if err := r.Delete(ctx, s.id, &runcC.DeleteOpts{
Force: true,
}); err != nil {
logrus.WithError(err).Warn("failed to remove runc container")
}
if err := mount.UnmountRecursive(filepath.Join(path, "rootfs"), 0); err != nil {
logrus.WithError(err).Warn("failed to cleanup rootfs mount")
}
pid, err := runcC.ReadPidFile(filepath.Join(path, process.InitPidFile))
if err != nil {
logrus.WithError(err).Warn("failed to read init pid file")
}
return &taskAPI.DeleteResponse{
ExitedAt: protobuf.ToTimestamp(time.Now()),
ExitStatus: 128 + uint32(unix.SIGKILL),
Pid: uint32(pid),
}, nil
}
// 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.container = 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
}
// Start a process
func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.StartResponse, error) {
container, err := s.getContainer()
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 "":
if cg, ok := container.Cgroup().(cgroup1.Cgroup); ok {
if err := s.ep.Add(container.ID, cg); err != nil {
logrus.WithError(err).Error("add cg to OOM monitor")
}
} else {
logrus.WithError(errdefs.ErrNotImplemented).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()
if err != nil {
return nil, err
}
p, err := container.Delete(ctx, r)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
// if we deleted our init task, close the platform and send the task delete event
if r.ExecID == "" {
if s.platform != nil {
s.platform.Close()
}
s.send(&eventstypes.TaskDelete{
ContainerID: container.ID,
Pid: uint32(p.Pid()),
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: protobuf.ToTimestamp(p.ExitedAt()),
})
}
return &taskAPI.DeleteResponse{
ExitStatus: uint32(p.ExitStatus()),
ExitedAt: protobuf.ToTimestamp(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()
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: s.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()
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) {
p, err := s.getProcess(r.ExecID)
if err != nil {
return nil, err
}
st, err := p.Status(ctx)
if err != nil {
return nil, err
}
status := task.Status_UNKNOWN
switch st {
case "created":
status = task.Status_CREATED
case "running":
status = task.Status_RUNNING
case "stopped":
status = task.Status_STOPPED
case "paused":
status = task.Status_PAUSED
case "pausing":
status = task.Status_PAUSING
}
sio := p.Stdio()
return &taskAPI.StateResponse{
ID: p.ID(),
Bundle: s.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: protobuf.ToTimestamp(p.ExitedAt()),
}, nil
}
// Pause the container
func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.Empty, error) {
container, err := s.getContainer()
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()
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()
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()
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 := protobuf.MarshalAnyToProto(d)
if err != nil {
return nil, fmt.Errorf("failed to marshal process %d info: %w", pid, err)
}
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()
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()
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()
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()
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: protobuf.ToTimestamp(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 s.container != nil {
pid = s.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) {
// please make sure that temporary resource has been cleanup
// before shutdown service.
s.cancel()
close(s.events)
return empty, nil
}
func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
container, err := s.getContainer()
if err != nil {
return nil, err
}
cgx := container.Cgroup()
if cgx == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
}
cg, ok := cgx.(cgroup1.Cgroup)
if !ok {
return nil, errdefs.ToGRPCf(errdefs.ErrNotImplemented, "cgroup v2 not implemented for Stats")
}
if cg == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
}
stats, err := cg.Stat(cgroup1.IgnoreNotExist)
if err != nil {
return nil, err
}
data, err := typeurl.MarshalAny(stats)
if err != nil {
return nil, err
}
return &taskAPI.StatsResponse{
Stats: protobuf.FromAny(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) {
container, err := s.getContainer()
if err != nil {
return
}
for _, p := range container.All() {
if p.Pid() == e.Pid {
if runc.ShouldKillAllOnExit(s.context, container.Bundle) {
if ip, ok := p.(*process.Init); ok {
// Ensure all children are killed
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: protobuf.ToTimestamp(p.ExitedAt()),
})
return
}
}
}
func (s *service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
p, err := s.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 {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
err := publisher.Publish(ctx, runc.GetTopic(e), e)
cancel()
if err != nil {
logrus.WithError(err).Error("post event")
}
}
publisher.Close()
}
func (s *service) getContainer() (*runc.Container, error) {
s.mu.Lock()
container := s.container
s.mu.Unlock()
if container == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "container not created")
}
return container, nil
}
func (s *service) getProcess(execID string) (process.Process, error) {
container, err := s.getContainer()
if err != nil {
return nil, err
}
p, err := container.Process(execID)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
return p, 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
return nil
}

View File

@ -23,7 +23,6 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"time" "time"
api "github.com/containerd/containerd/api/services/tasks/v1" api "github.com/containerd/containerd/api/services/tasks/v1"
@ -47,7 +46,6 @@ import (
"github.com/containerd/containerd/protobuf/proto" "github.com/containerd/containerd/protobuf/proto"
ptypes "github.com/containerd/containerd/protobuf/types" ptypes "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/containerd/services" "github.com/containerd/containerd/services"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
@ -89,10 +87,6 @@ func init() {
func initFunc(ic *plugin.InitContext) (interface{}, error) { func initFunc(ic *plugin.InitContext) (interface{}, error) {
config := ic.Config.(*Config) config := ic.Config.(*Config)
runtimes, err := loadV1Runtimes(ic)
if err != nil {
return nil, err
}
v2r, err := ic.GetByID(plugin.RuntimePluginV2, "task") v2r, err := ic.GetByID(plugin.RuntimePluginV2, "task")
if err != nil { if err != nil {
@ -119,22 +113,13 @@ func initFunc(ic *plugin.InitContext) (interface{}, error) {
db := m.(*metadata.DB) db := m.(*metadata.DB)
l := &local{ l := &local{
runtimes: runtimes,
containers: metadata.NewContainerStore(db), containers: metadata.NewContainerStore(db),
store: db.ContentStore(), store: db.ContentStore(),
publisher: ep.(events.Publisher), publisher: ep.(events.Publisher),
monitor: monitor.(runtime.TaskMonitor), monitor: monitor.(runtime.TaskMonitor),
v2Runtime: v2r.(runtime.PlatformRuntime), v2Runtime: v2r.(runtime.PlatformRuntime),
} }
for _, r := range runtimes {
tasks, err := r.Tasks(ic.Context, true)
if err != nil {
return nil, err
}
for _, t := range tasks {
l.monitor.Monitor(t, nil)
}
}
v2Tasks, err := l.v2Runtime.Tasks(ic.Context, true) v2Tasks, err := l.v2Runtime.Tasks(ic.Context, true)
if err != nil { if err != nil {
return nil, err return nil, err
@ -154,7 +139,6 @@ func initFunc(ic *plugin.InitContext) (interface{}, error) {
} }
type local struct { type local struct {
runtimes map[string]runtime.PlatformRuntime
containers containers.Store containers containers.Store
store content.Store store content.Store
publisher events.Publisher publisher events.Publisher
@ -221,15 +205,9 @@ func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.
Options: m.Options, Options: m.Options,
}) })
} }
if strings.HasPrefix(container.Runtime.Name, "io.containerd.runtime.v1.") {
log.G(ctx).Warn("runtime v1 is deprecated since containerd v1.4, consider using runtime v2") rtime := l.v2Runtime
} else if container.Runtime.Name == plugin.RuntimeRuncV1 {
log.G(ctx).Warnf("%q is deprecated since containerd v1.4, consider using %q", plugin.RuntimeRuncV1, plugin.RuntimeRuncV2)
}
rtime, err := l.getRuntime(container.Runtime.Name)
if err != nil {
return nil, err
}
_, err = rtime.Get(ctx, r.ContainerID) _, err = rtime.Get(ctx, r.ContainerID)
if err != nil && !errdefs.IsNotFound(err) { if err != nil && !errdefs.IsNotFound(err) {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
@ -284,14 +262,8 @@ func (l *local) Delete(ctx context.Context, r *api.DeleteTaskRequest, _ ...grpc.
return nil, err return nil, err
} }
// Find runtime manager
rtime, err := l.getRuntime(container.Runtime.Name)
if err != nil {
return nil, err
}
// Get task object // Get task object
t, err := rtime.Get(ctx, container.ID) t, err := l.v2Runtime.Get(ctx, container.ID)
if err != nil { if err != nil {
return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID) return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID)
} }
@ -300,7 +272,7 @@ func (l *local) Delete(ctx context.Context, r *api.DeleteTaskRequest, _ ...grpc.
return nil, err return nil, err
} }
exit, err := rtime.Delete(ctx, r.ContainerID) exit, err := l.v2Runtime.Delete(ctx, r.ContainerID)
if err != nil { if err != nil {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
} }
@ -394,13 +366,11 @@ func (l *local) Get(ctx context.Context, r *api.GetRequest, _ ...grpc.CallOption
func (l *local) List(ctx context.Context, r *api.ListTasksRequest, _ ...grpc.CallOption) (*api.ListTasksResponse, error) { func (l *local) List(ctx context.Context, r *api.ListTasksRequest, _ ...grpc.CallOption) (*api.ListTasksResponse, error) {
resp := &api.ListTasksResponse{} resp := &api.ListTasksResponse{}
for _, r := range l.allRuntimes() { tasks, err := l.v2Runtime.Tasks(ctx, false)
tasks, err := r.Tasks(ctx, false) if err != nil {
if err != nil { return nil, errdefs.ToGRPC(err)
return nil, errdefs.ToGRPC(err)
}
addTasks(ctx, resp, tasks)
} }
addTasks(ctx, resp, tasks)
return resp, nil return resp, nil
} }
@ -623,13 +593,11 @@ func (l *local) Metrics(ctx context.Context, r *api.MetricsRequest, _ ...grpc.Ca
return nil, err return nil, err
} }
var resp api.MetricsResponse var resp api.MetricsResponse
for _, r := range l.allRuntimes() { tasks, err := l.v2Runtime.Tasks(ctx, false)
tasks, err := r.Tasks(ctx, false) if err != nil {
if err != nil { return nil, err
return nil, err
}
getTasksMetrics(ctx, filter, tasks, &resp)
} }
getTasksMetrics(ctx, filter, tasks, &resp)
return &resp, nil return &resp, nil
} }
@ -725,34 +693,13 @@ func (l *local) getTask(ctx context.Context, id string) (runtime.Task, error) {
} }
func (l *local) getTaskFromContainer(ctx context.Context, container *containers.Container) (runtime.Task, error) { func (l *local) getTaskFromContainer(ctx context.Context, container *containers.Container) (runtime.Task, error) {
runtime, err := l.getRuntime(container.Runtime.Name) t, err := l.v2Runtime.Get(ctx, container.ID)
if err != nil {
return nil, errdefs.ToGRPCf(err, "runtime for task %s", container.Runtime.Name)
}
t, err := runtime.Get(ctx, container.ID)
if err != nil { if err != nil {
return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID) return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID)
} }
return t, nil return t, nil
} }
func (l *local) getRuntime(name string) (runtime.PlatformRuntime, error) {
runtime, ok := l.runtimes[name]
if !ok {
// one runtime to rule them all
return l.v2Runtime, nil
}
return runtime, nil
}
func (l *local) allRuntimes() (o []runtime.PlatformRuntime) {
for _, r := range l.runtimes {
o = append(o, r)
}
o = append(o, l.v2Runtime)
return o
}
// getCheckpointPath only suitable for runc runtime now // getCheckpointPath only suitable for runc runtime now
func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) { func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) {
if option == nil { if option == nil {
@ -760,29 +707,15 @@ func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) {
} }
var checkpointPath string var checkpointPath string
switch { v, err := typeurl.UnmarshalAny(option)
case checkRuntime(runtime, "io.containerd.runc"): if err != nil {
v, err := typeurl.UnmarshalAny(option) return "", err
if err != nil {
return "", err
}
opts, ok := v.(*options.CheckpointOptions)
if !ok {
return "", fmt.Errorf("invalid task checkpoint option for %s", runtime)
}
checkpointPath = opts.ImagePath
case runtime == plugin.RuntimeLinuxV1:
v, err := typeurl.UnmarshalAny(option)
if err != nil {
return "", err
}
opts, ok := v.(*runctypes.CheckpointOptions)
if !ok {
return "", fmt.Errorf("invalid task checkpoint option for %s", runtime)
}
checkpointPath = opts.ImagePath
} }
opts, ok := v.(*options.CheckpointOptions)
if !ok {
return "", fmt.Errorf("invalid task checkpoint option for %s", runtime)
}
checkpointPath = opts.ImagePath
return checkpointPath, nil return checkpointPath, nil
} }
@ -794,45 +727,15 @@ func getRestorePath(runtime string, option *ptypes.Any) (string, error) {
} }
var restorePath string var restorePath string
switch { v, err := typeurl.UnmarshalAny(option)
case checkRuntime(runtime, "io.containerd.runc"): if err != nil {
v, err := typeurl.UnmarshalAny(option) return "", err
if err != nil {
return "", err
}
opts, ok := v.(*options.Options)
if !ok {
return "", fmt.Errorf("invalid task create option for %s", runtime)
}
restorePath = opts.CriuImagePath
case runtime == plugin.RuntimeLinuxV1:
v, err := typeurl.UnmarshalAny(option)
if err != nil {
return "", err
}
opts, ok := v.(*runctypes.CreateOptions)
if !ok {
return "", fmt.Errorf("invalid task create option for %s", runtime)
}
restorePath = opts.CriuImagePath
} }
opts, ok := v.(*options.Options)
if !ok {
return "", fmt.Errorf("invalid task create option for %s", runtime)
}
restorePath = opts.CriuImagePath
return restorePath, nil return restorePath, nil
} }
// checkRuntime returns true if the current runtime matches the expected
// runtime. Providing various parts of the runtime schema will match those
// parts of the expected runtime
func checkRuntime(current, expected string) bool {
cp := strings.Split(current, ".")
l := len(cp)
for i, p := range strings.Split(expected, ".") {
if i > l {
return false
}
if p != cp[i] {
return false
}
}
return true
}

View File

@ -20,7 +20,6 @@ package tasks
import ( import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime"
) )
var tasksServiceRequires = []plugin.Type{ var tasksServiceRequires = []plugin.Type{
@ -28,8 +27,3 @@ var tasksServiceRequires = []plugin.Type{
plugin.MetadataPlugin, plugin.MetadataPlugin,
plugin.TaskMonitorPlugin, plugin.TaskMonitorPlugin,
} }
// loadV1Runtimes on darwin returns an empty map. There are no v1 runtimes
func loadV1Runtimes(ic *plugin.InitContext) (map[string]runtime.PlatformRuntime, error) {
return make(map[string]runtime.PlatformRuntime), nil
}

View File

@ -18,7 +18,6 @@ package tasks
import ( import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime"
) )
var tasksServiceRequires = []plugin.Type{ var tasksServiceRequires = []plugin.Type{
@ -27,8 +26,3 @@ var tasksServiceRequires = []plugin.Type{
plugin.MetadataPlugin, plugin.MetadataPlugin,
plugin.TaskMonitorPlugin, plugin.TaskMonitorPlugin,
} }
// loadV1Runtimes on FreeBSD returns an empty map. There are no v1 runtimes
func loadV1Runtimes(ic *plugin.InitContext) (map[string]runtime.PlatformRuntime, error) {
return make(map[string]runtime.PlatformRuntime), nil
}

View File

@ -19,11 +19,7 @@
package tasks package tasks
import ( import (
"errors"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime"
) )
var tasksServiceRequires = []plugin.Type{ var tasksServiceRequires = []plugin.Type{
@ -33,26 +29,3 @@ var tasksServiceRequires = []plugin.Type{
plugin.MetadataPlugin, plugin.MetadataPlugin,
plugin.TaskMonitorPlugin, plugin.TaskMonitorPlugin,
} }
func loadV1Runtimes(ic *plugin.InitContext) (map[string]runtime.PlatformRuntime, error) {
rt, err := ic.GetByType(plugin.RuntimePlugin)
if err != nil {
return nil, err
}
runtimes := make(map[string]runtime.PlatformRuntime)
for _, rr := range rt {
ri, err := rr.Instance()
if err != nil {
log.G(ic.Context).WithError(err).Warn("could not load runtime instance due to initialization error")
continue
}
r := ri.(runtime.PlatformRuntime)
runtimes[r.ID()] = r
}
if len(runtimes) == 0 {
return nil, errors.New("no runtimes available to create task service")
}
return runtimes, nil
}

View File

@ -18,7 +18,6 @@ package tasks
import ( import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime"
) )
var tasksServiceRequires = []plugin.Type{ var tasksServiceRequires = []plugin.Type{
@ -27,8 +26,3 @@ var tasksServiceRequires = []plugin.Type{
plugin.MetadataPlugin, plugin.MetadataPlugin,
plugin.TaskMonitorPlugin, plugin.TaskMonitorPlugin,
} }
// loadV1Runtimes on Windows V2 returns an empty map. There are no v1 runtimes
func loadV1Runtimes(ic *plugin.InitContext) (map[string]runtime.PlatformRuntime, error) {
return make(map[string]runtime.PlatformRuntime), nil
}

View File

@ -41,7 +41,6 @@ import (
"github.com/containerd/containerd/protobuf" "github.com/containerd/containerd/protobuf"
google_protobuf "github.com/containerd/containerd/protobuf/types" google_protobuf "github.com/containerd/containerd/protobuf/types"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl/v2" "github.com/containerd/typeurl/v2"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
@ -691,15 +690,10 @@ func isCheckpointPathExist(runtime string, v interface{}) bool {
} }
switch runtime { switch runtime {
case plugin.RuntimeRuncV1, plugin.RuntimeRuncV2: case plugin.RuntimeRuncV2:
if opts, ok := v.(*options.CheckpointOptions); ok && opts.ImagePath != "" { if opts, ok := v.(*options.CheckpointOptions); ok && opts.ImagePath != "" {
return true return true
} }
case plugin.RuntimeLinuxV1:
if opts, ok := v.(*runctypes.CheckpointOptions); ok && opts.ImagePath != "" {
return true
}
} }
return false return false

View File

@ -28,7 +28,6 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
imagespec "github.com/opencontainers/image-spec/specs-go/v1" imagespec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
@ -104,25 +103,14 @@ func WithCheckpointName(name string) CheckpointTaskOpts {
// WithCheckpointImagePath sets image path for checkpoint option // WithCheckpointImagePath sets image path for checkpoint option
func WithCheckpointImagePath(path string) CheckpointTaskOpts { func WithCheckpointImagePath(path string) CheckpointTaskOpts {
return func(r *CheckpointTaskInfo) error { return func(r *CheckpointTaskInfo) error {
if CheckRuntime(r.Runtime(), "io.containerd.runc") { if r.Options == nil {
if r.Options == nil { r.Options = &options.CheckpointOptions{}
r.Options = &options.CheckpointOptions{}
}
opts, ok := r.Options.(*options.CheckpointOptions)
if !ok {
return errors.New("invalid v2 shim checkpoint options format")
}
opts.ImagePath = path
} else {
if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{}
}
opts, ok := r.Options.(*runctypes.CheckpointOptions)
if !ok {
return errors.New("invalid v1 shim checkpoint options format")
}
opts.ImagePath = path
} }
opts, ok := r.Options.(*options.CheckpointOptions)
if !ok {
return errors.New("invalid v2 shim checkpoint options format")
}
opts.ImagePath = path
return nil return nil
} }
} }
@ -130,25 +118,14 @@ func WithCheckpointImagePath(path string) CheckpointTaskOpts {
// WithRestoreImagePath sets image path for create option // WithRestoreImagePath sets image path for create option
func WithRestoreImagePath(path string) NewTaskOpts { func WithRestoreImagePath(path string) NewTaskOpts {
return func(ctx context.Context, c *Client, ti *TaskInfo) error { return func(ctx context.Context, c *Client, ti *TaskInfo) error {
if CheckRuntime(ti.Runtime(), "io.containerd.runc") { if ti.Options == nil {
if ti.Options == nil { ti.Options = &options.Options{}
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.CriuImagePath = path
} else {
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{}
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("invalid v1 shim create options format")
}
opts.CriuImagePath = path
} }
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.CriuImagePath = path
return nil return nil
} }
} }

View File

@ -22,84 +22,47 @@ import (
"context" "context"
"errors" "errors"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options" "github.com/containerd/containerd/runtime/v2/runc/options"
) )
// WithNoNewKeyring causes tasks not to be created with a new keyring for secret storage. // WithNoNewKeyring causes tasks not to be created with a new keyring for secret storage.
// There is an upper limit on the number of keyrings in a linux system // There is an upper limit on the number of keyrings in a linux system
func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error {
if CheckRuntime(ti.Runtime(), "io.containerd.runc") { if ti.Options == nil {
if ti.Options == nil { ti.Options = &options.Options{}
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.NoNewKeyring = true
} else {
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{}
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("could not cast TaskInfo Options to CreateOptions")
}
opts.NoNewKeyring = true
} }
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.NoNewKeyring = true
return nil return nil
} }
// WithNoPivotRoot instructs the runtime not to you pivot_root // WithNoPivotRoot instructs the runtime not to you pivot_root
func WithNoPivotRoot(_ context.Context, _ *Client, ti *TaskInfo) error { func WithNoPivotRoot(_ context.Context, _ *Client, ti *TaskInfo) error {
if CheckRuntime(ti.Runtime(), "io.containerd.runc") { if ti.Options == nil {
if ti.Options == nil { ti.Options = &options.Options{}
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.NoPivotRoot = true
} else {
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{
NoPivotRoot: true,
}
return nil
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("invalid options type, expected runctypes.CreateOptions")
}
opts.NoPivotRoot = true
} }
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.NoPivotRoot = true
return nil return nil
} }
// WithShimCgroup sets the existing cgroup for the shim // WithShimCgroup sets the existing cgroup for the shim
func WithShimCgroup(path string) NewTaskOpts { func WithShimCgroup(path string) NewTaskOpts {
return func(ctx context.Context, c *Client, ti *TaskInfo) error { return func(ctx context.Context, c *Client, ti *TaskInfo) error {
if CheckRuntime(ti.Runtime(), "io.containerd.runc") { if ti.Options == nil {
if ti.Options == nil { ti.Options = &options.Options{}
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.ShimCgroup = path
} else {
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{}
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("could not cast TaskInfo Options to CreateOptions")
}
opts.ShimCgroup = path
} }
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.ShimCgroup = path
return nil return nil
} }
} }
@ -107,25 +70,14 @@ func WithShimCgroup(path string) NewTaskOpts {
// WithUIDOwner allows console I/O to work with the remapped UID in user namespace // WithUIDOwner allows console I/O to work with the remapped UID in user namespace
func WithUIDOwner(uid uint32) NewTaskOpts { func WithUIDOwner(uid uint32) NewTaskOpts {
return func(ctx context.Context, c *Client, ti *TaskInfo) error { return func(ctx context.Context, c *Client, ti *TaskInfo) error {
if CheckRuntime(ti.Runtime(), "io.containerd.runc") { if ti.Options == nil {
if ti.Options == nil { ti.Options = &options.Options{}
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.IoUid = uid
} else {
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{}
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("could not cast TaskInfo Options to CreateOptions")
}
opts.IoUid = uid
} }
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.IoUid = uid
return nil return nil
} }
} }
@ -133,25 +85,14 @@ func WithUIDOwner(uid uint32) NewTaskOpts {
// WithGIDOwner allows console I/O to work with the remapped GID in user namespace // WithGIDOwner allows console I/O to work with the remapped GID in user namespace
func WithGIDOwner(gid uint32) NewTaskOpts { func WithGIDOwner(gid uint32) NewTaskOpts {
return func(ctx context.Context, c *Client, ti *TaskInfo) error { return func(ctx context.Context, c *Client, ti *TaskInfo) error {
if CheckRuntime(ti.Runtime(), "io.containerd.runc") { if ti.Options == nil {
if ti.Options == nil { ti.Options = &options.Options{}
ti.Options = &options.Options{}
}
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.IoGid = gid
} else {
if ti.Options == nil {
ti.Options = &runctypes.CreateOptions{}
}
opts, ok := ti.Options.(*runctypes.CreateOptions)
if !ok {
return errors.New("could not cast TaskInfo Options to CreateOptions")
}
opts.IoGid = gid
} }
opts, ok := ti.Options.(*options.Options)
if !ok {
return errors.New("invalid v2 shim create options format")
}
opts.IoGid = gid
return nil return nil
} }
} }