Merge pull request #8262 from mxpv/v1
🪦 Remove `io.containerd.runtime.v1.linux` and `io.containerd.runc.v1`
This commit is contained in:
commit
f558a3d598
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -390,16 +390,9 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
runtime:
|
||||
- io.containerd.runtime.v1.linux
|
||||
- io.containerd.runc.v1
|
||||
- io.containerd.runc.v2
|
||||
runc: [runc, crun]
|
||||
enable_cri_sandboxes: ["", "sandboxed"]
|
||||
exclude:
|
||||
- runtime: io.containerd.runc.v1
|
||||
runc: crun
|
||||
- runtime: io.containerd.runtime.v1.linux
|
||||
runc: crun
|
||||
|
||||
env:
|
||||
GOTEST: gotestsum --
|
||||
|
10
Makefile
10
Makefile
@ -254,14 +254,6 @@ bin/gen-manpages: cmd/gen-manpages FORCE
|
||||
@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
|
||||
|
||||
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
|
||||
@echo "$(WHALE) $@"
|
||||
@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
|
||||
@echo "$(WHALE) $@"
|
||||
$(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 \
|
||||
grep $$container /proc/self/mountinfo | while read -r mountpoint; do \
|
||||
umount $$(echo $$mountpoint | awk '{print $$5}'); \
|
||||
|
@ -13,9 +13,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
#darwin specific settings
|
||||
COMMANDS += containerd-shim
|
||||
|
||||
# amd64 supports go test -race
|
||||
ifeq ($(GOARCH),amd64)
|
||||
TESTFLAGS_RACE= -race
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
|
||||
#freebsd specific settings
|
||||
COMMANDS += containerd-shim
|
||||
|
||||
# amd64 supports go test -race
|
||||
ifeq ($(GOARCH),amd64)
|
||||
|
@ -16,7 +16,7 @@
|
||||
#linux specific settings
|
||||
WHALE="+"
|
||||
ONI="-"
|
||||
COMMANDS += containerd-shim containerd-shim-runc-v1 containerd-shim-runc-v2
|
||||
COMMANDS += containerd-shim-runc-v2
|
||||
|
||||
# check GOOS for cross compile builds
|
||||
ifeq ($(GOOS),linux)
|
||||
|
@ -28,14 +28,6 @@ prefixes = [
|
||||
]
|
||||
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]]
|
||||
prefix = "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
target = "runtime/v2/runc/options/next.pb.txt"
|
||||
|
14
RELEASES.md
14
RELEASES.md
@ -371,8 +371,8 @@ The deprecated features are shown in the following table:
|
||||
|
||||
| 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` |
|
||||
| Runc V1 implementation of Runtime V2 (`io.containerd.runc.v1`) | 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` |
|
||||
| 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 |
|
||||
| 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 |
|
||||
|----------------------------------------------------------------------|------------------------------|---------------------|----------------------------|-------------------------------------------------|
|
||||
|`[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".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.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]` | `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.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.*.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]` | `configs` | containerd v1.5 | containerd v2.0 | Use [`config_path`](./docs/hosts.md) |
|
||||
|
17
client.go
17
client.go
@ -818,23 +818,6 @@ func (c *Client) getSnapshotter(ctx context.Context, name string) (snapshots.Sna
|
||||
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
|
||||
// supported platforms for the given snapshotters
|
||||
func (c *Client) GetSnapshotterSupportedPlatforms(ctx context.Context, snapshotterName string) (platforms.MatchComparer, error) {
|
||||
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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()
|
||||
}
|
@ -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()
|
||||
}
|
@ -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()))
|
||||
}
|
@ -19,7 +19,6 @@ package builtins
|
||||
import (
|
||||
_ "github.com/containerd/containerd/metrics/cgroups"
|
||||
_ "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/snapshots/native/plugin"
|
||||
_ "github.com/containerd/containerd/snapshots/overlay/plugin"
|
||||
|
@ -22,8 +22,6 @@ import (
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"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/urfave/cli"
|
||||
)
|
||||
@ -86,38 +84,21 @@ func withCheckpointOpts(rt string, context *cli.Context) containerd.CheckpointTa
|
||||
imagePath := context.String("image-path")
|
||||
workPath := context.String("work-path")
|
||||
|
||||
switch rt {
|
||||
case plugin.RuntimeRuncV1, plugin.RuntimeRuncV2:
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ containerd_extra_runtime_handler=${CONTAINERD_EXTRA_RUNTIME_HANDLER:-""}
|
||||
if [[ -n "${containerd_extra_runtime_handler}" ]]; then
|
||||
cat >> ${config_path} <<EOF
|
||||
[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]
|
||||
${CONTAINERD_EXTRA_RUNTIME_OPTIONS:-}
|
||||
|
@ -22,7 +22,7 @@ For each `containerd` release, we'll publish a release tarball specifically for
|
||||
### Content
|
||||
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.
|
||||
- `/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.
|
||||
|
@ -18,8 +18,7 @@ package integration
|
||||
|
||||
import (
|
||||
// Register for linux platforms
|
||||
_ "github.com/containerd/containerd/plugins/sandbox" // WithInMemoryServices will fail otherwise
|
||||
_ "github.com/containerd/containerd/runtime/v1/linux"
|
||||
_ "github.com/containerd/containerd/plugins/sandbox" // WithInMemoryServices will fail otherwise
|
||||
_ "github.com/containerd/containerd/services/sandbox" // WithInMemoryServices will fail otherwise
|
||||
_ "github.com/containerd/containerd/snapshots/overlay/plugin"
|
||||
)
|
||||
|
@ -32,7 +32,6 @@ import (
|
||||
. "github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/cio"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -48,9 +47,6 @@ func TestCheckpointRestorePTY(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if client.Runtime() == plugin.RuntimeLinuxV1 {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var (
|
||||
ctx, cancel = testContext(t)
|
||||
@ -174,9 +170,6 @@ func TestCheckpointRestore(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if client.Runtime() == plugin.RuntimeLinuxV1 {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var (
|
||||
ctx, cancel = testContext(t)
|
||||
@ -264,9 +257,6 @@ func TestCheckpointRestoreNewContainer(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if client.Runtime() == plugin.RuntimeLinuxV1 {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
id := t.Name()
|
||||
ctx, cancel := testContext(t)
|
||||
@ -354,9 +344,6 @@ func TestCheckpointLeaveRunning(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if client.Runtime() == plugin.RuntimeLinuxV1 {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var (
|
||||
ctx, cancel = testContext(t)
|
||||
@ -538,9 +525,6 @@ func TestCheckpointOnPauseStatus(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer client.Close()
|
||||
if client.Runtime() == plugin.RuntimeLinuxV1 {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
var (
|
||||
ctx, cancel = testContext(t)
|
||||
|
@ -39,7 +39,6 @@ import (
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/containerd/sys"
|
||||
"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.
|
||||
// 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(runtimeVersion, id)
|
||||
logDirPath := getLogDirPath("v2", id)
|
||||
|
||||
switch runtimeVersion {
|
||||
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))
|
||||
}
|
||||
writeToFile(t, filepath.Join(logDirPath, "log"), fmt.Sprintf("%s writing to log\n", id))
|
||||
|
||||
statusC, err := task.Wait(ctx)
|
||||
if err != nil {
|
||||
@ -402,18 +394,8 @@ func TestDaemonReconnectsToShimIOPipesOnRestart(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch runtimeVersion {
|
||||
case "v1":
|
||||
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")
|
||||
}
|
||||
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 {
|
||||
switch runtimeVersion {
|
||||
case "v1":
|
||||
return filepath.Join(defaultRoot, plugin.RuntimeLinuxV1, testNamespace, id)
|
||||
case "v2":
|
||||
return filepath.Join(defaultState, "io.containerd.runtime.v2.task", testNamespace, id)
|
||||
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) {
|
||||
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) {
|
||||
t.Parallel()
|
||||
|
||||
@ -1099,7 +1027,7 @@ func TestContainerRuntimeOptionsv2(t *testing.T) {
|
||||
ctx, id,
|
||||
WithNewSnapshot(id, image),
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
@ -1187,17 +1115,9 @@ func testUserNamespaces(t *testing.T, readonlyRootFS bool) {
|
||||
}
|
||||
defer container.Delete(ctx, WithSnapshotCleanup)
|
||||
|
||||
var copts interface{}
|
||||
if CheckRuntime(client.Runtime(), "io.containerd.runc") {
|
||||
copts = &options.Options{
|
||||
IoUid: 1000,
|
||||
IoGid: 2000,
|
||||
}
|
||||
} else {
|
||||
copts = &runctypes.CreateOptions{
|
||||
IoUid: 1000,
|
||||
IoGid: 2000,
|
||||
}
|
||||
copts := &options.Options{
|
||||
IoUid: 1000,
|
||||
IoGid: 2000,
|
||||
}
|
||||
|
||||
task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio), func(_ context.Context, client *Client, r *TaskInfo) error {
|
||||
|
@ -39,7 +39,6 @@ import (
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/platforms"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
gogotypes "github.com/containerd/containerd/protobuf/types"
|
||||
_ "github.com/containerd/containerd/runtime"
|
||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
@ -670,10 +669,6 @@ func TestKillContainerDeletedByRunc(t *testing.T) {
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
if client.Runtime() == plugin.RuntimeLinuxV1 {
|
||||
t.Skip("test relies on runtime v2")
|
||||
}
|
||||
|
||||
var (
|
||||
image Image
|
||||
ctx, cancel = testContext(t)
|
||||
|
@ -55,7 +55,7 @@ version = 2
|
||||
}
|
||||
|
||||
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,
|
||||
}))
|
||||
if err != nil {
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -30,7 +30,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v1shimcli "github.com/containerd/containerd/runtime/v1/shim/client"
|
||||
v2shimcli "github.com/containerd/containerd/runtime/v2/shim"
|
||||
"github.com/containerd/ttrpc"
|
||||
)
|
||||
@ -47,10 +46,8 @@ func TestFailFastWhenConnectShim(t *testing.T) {
|
||||
|
||||
// abstract Unix domain sockets are only for 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("normal-unix-socket-v1", testFailFastWhenConnectShim(false, v1shimcli.AnonDialer))
|
||||
t.Run("normal-unix-socket-v2", testFailFastWhenConnectShim(false, v2shimcli.AnonDialer))
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,6 @@ import (
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
"github.com/containerd/containerd/runtime/v1/linux"
|
||||
"github.com/docker/go-metrics"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -55,11 +54,15 @@ type cgroupsMonitor struct {
|
||||
publisher events.Publisher
|
||||
}
|
||||
|
||||
type cgroupTask interface {
|
||||
Cgroup() (cgroups.Cgroup, error)
|
||||
}
|
||||
|
||||
func (m *cgroupsMonitor) Monitor(c runtime.Task, labels map[string]string) error {
|
||||
if err := m.collector.Add(c, labels); err != nil {
|
||||
return err
|
||||
}
|
||||
t, ok := c.(*linux.Task)
|
||||
t, ok := c.(cgroupTask)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
)
|
||||
|
||||
type SandboxControllerMode string
|
||||
@ -46,10 +45,6 @@ type Runtime struct {
|
||||
// When specified, containerd will ignore runtime name field when resolving shim location.
|
||||
// Path must be abs.
|
||||
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
|
||||
// container OCI annotations.
|
||||
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).
|
||||
// Currently, only device plugins populate the annotations.
|
||||
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.
|
||||
// If options is loaded from toml config, it will be map[string]interface{}.
|
||||
// Options can be converted into toml.Tree using toml.TreeFromMap().
|
||||
@ -99,19 +90,10 @@ type ContainerdConfig struct {
|
||||
Snapshotter string `toml:"snapshotter" json:"snapshotter"`
|
||||
// DefaultRuntimeName is the default runtime name to use from the runtimes table.
|
||||
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
|
||||
// configurations, to the matching configurations.
|
||||
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
|
||||
// related information) to snapshotters. These annotations are required by
|
||||
@ -274,10 +256,6 @@ type PluginConfig struct {
|
||||
SandboxImage string `toml:"sandbox_image" json:"sandboxImage"`
|
||||
// StatsCollectPeriod is the period (in seconds) of snapshots stats collection.
|
||||
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 bool `toml:"enable_tls_streaming" json:"enableTLSStreaming"`
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
if c.ContainerdConfig.DefaultRuntimeName == "" {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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 {
|
||||
return errors.New("`privileged_without_host_devices_all_devices_allowed` requires `privileged_without_host_devices` to be enabled")
|
||||
}
|
||||
|
@ -18,10 +18,8 @@ package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -31,81 +29,6 @@ func TestValidateConfig(t *testing.T) {
|
||||
expectedErr string
|
||||
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": {
|
||||
config: &PluginConfig{},
|
||||
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\"",
|
||||
},
|
||||
"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": {
|
||||
config: &PluginConfig{
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
DefaultRuntimeName: RuntimeDefault,
|
||||
Runtimes: map[string]Runtime{
|
||||
RuntimeDefault: {
|
||||
Type: plugin.RuntimeRuncV1,
|
||||
},
|
||||
RuntimeDefault: {},
|
||||
},
|
||||
},
|
||||
Registry: Registry{
|
||||
@ -295,7 +61,6 @@ func TestValidateConfig(t *testing.T) {
|
||||
DefaultRuntimeName: RuntimeDefault,
|
||||
Runtimes: map[string]Runtime{
|
||||
RuntimeDefault: {
|
||||
Type: plugin.RuntimeRuncV1,
|
||||
SandboxMode: string(ModePodSandbox),
|
||||
},
|
||||
},
|
||||
|
@ -29,9 +29,6 @@ import (
|
||||
// DefaultConfig returns default configurations of cri plugin.
|
||||
func DefaultConfig() PluginConfig {
|
||||
defaultRuncV2Opts := `
|
||||
# NoPivotRoot disables pivot root when creating a container.
|
||||
NoPivotRoot = false
|
||||
|
||||
# NoNewKeyring disables new keyring for the container.
|
||||
NoNewKeyring = false
|
||||
|
||||
@ -53,9 +50,6 @@ func DefaultConfig() PluginConfig {
|
||||
# CriuPath is the criu binary path.
|
||||
CriuPath = ""
|
||||
|
||||
# SystemdCgroup enables systemd cgroups.
|
||||
SystemdCgroup = false
|
||||
|
||||
# CriuImagePath is the criu image path
|
||||
CriuImagePath = ""
|
||||
|
||||
@ -74,7 +68,6 @@ func DefaultConfig() PluginConfig {
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
Snapshotter: containerd.DefaultSnapshotter,
|
||||
DefaultRuntimeName: "runc",
|
||||
NoPivot: false,
|
||||
Runtimes: map[string]Runtime{
|
||||
"runc": {
|
||||
Type: "io.containerd.runc.v2",
|
||||
@ -97,7 +90,6 @@ func DefaultConfig() PluginConfig {
|
||||
},
|
||||
SandboxImage: "registry.k8s.io/pause:3.8",
|
||||
StatsCollectPeriod: 10,
|
||||
SystemdCgroup: false,
|
||||
MaxContainerLogLineSize: 16 * 1024,
|
||||
MaxConcurrentDownloads: 3,
|
||||
DisableProcMount: false,
|
||||
|
@ -38,7 +38,6 @@ func DefaultConfig() PluginConfig {
|
||||
ContainerdConfig: ContainerdConfig{
|
||||
Snapshotter: containerd.DefaultSnapshotter,
|
||||
DefaultRuntimeName: "runhcs-wcow-process",
|
||||
NoPivot: false,
|
||||
Runtimes: map[string]Runtime{
|
||||
"runhcs-wcow-process": {
|
||||
Type: "io.containerd.runhcs.v1",
|
||||
|
@ -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.
|
@ -110,17 +110,12 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get sandbox runtime: %w", err)
|
||||
}
|
||||
|
||||
taskOpts := c.taskOpts(ctrInfo.Runtime.Name)
|
||||
var taskOpts []containerd.NewTaskOpts
|
||||
if ociRuntime.Path != "" {
|
||||
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ import (
|
||||
runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
runcoptions "github.com/containerd/containerd/runtime/v2/runc/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.
|
||||
func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) {
|
||||
if r.Options == nil {
|
||||
if r.Type != plugin.RuntimeLinuxV1 {
|
||||
return nil, nil
|
||||
}
|
||||
// This is a legacy config, generate runctypes.RuncOptions.
|
||||
return &runctypes.RuncOptions{
|
||||
Runtime: r.Engine,
|
||||
RuntimeRoot: r.Root,
|
||||
SystemdCgroup: c.SystemdCgroup,
|
||||
}, nil
|
||||
return nil, nil
|
||||
}
|
||||
optionsTree, err := toml.TreeFromMap(r.Options)
|
||||
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.
|
||||
func getRuntimeOptionsType(t string) interface{} {
|
||||
switch t {
|
||||
case plugin.RuntimeRuncV1:
|
||||
fallthrough
|
||||
case plugin.RuntimeRuncV2:
|
||||
return &runcoptions.Options{}
|
||||
case plugin.RuntimeLinuxV1:
|
||||
return &runctypes.RuncOptions{}
|
||||
case runtimeRunhcsV1:
|
||||
return &runhcsoptions.Options{}
|
||||
default:
|
||||
|
@ -35,7 +35,6 @@ import (
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/protobuf/types"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
|
||||
@ -210,10 +209,6 @@ systemd_cgroup = true
|
||||
[containerd]
|
||||
no_pivot = true
|
||||
default_runtime_name = "default"
|
||||
[containerd.runtimes.legacy]
|
||||
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
|
||||
[containerd.runtimes.runc]
|
||||
runtime_type = "` + plugin.RuntimeRuncV1 + `"
|
||||
[containerd.runtimes.runcv2]
|
||||
runtime_type = "` + plugin.RuntimeRuncV2 + `"
|
||||
`
|
||||
@ -222,13 +217,9 @@ systemd_cgroup = true
|
||||
[containerd]
|
||||
no_pivot = true
|
||||
default_runtime_name = "default"
|
||||
[containerd.runtimes.legacy]
|
||||
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
|
||||
[containerd.runtimes.legacy.options]
|
||||
Runtime = "legacy"
|
||||
RuntimeRoot = "/legacy"
|
||||
[containerd.runtimes.runc]
|
||||
runtime_type = "` + plugin.RuntimeRuncV1 + `"
|
||||
[containerd.runtimes.runc.options]
|
||||
BinaryName = "runc"
|
||||
Root = "/runc"
|
||||
@ -245,7 +236,7 @@ systemd_cgroup = true
|
||||
require.NoError(t, err)
|
||||
err = tree.Unmarshal(&nilOptsConfig)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nilOptsConfig.Runtimes, 3)
|
||||
require.Len(t, nilOptsConfig.Runtimes, 1)
|
||||
|
||||
tree, err = toml.Load(nonNilOpts)
|
||||
require.NoError(t, err)
|
||||
@ -258,32 +249,11 @@ systemd_cgroup = true
|
||||
c criconfig.Config
|
||||
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": {
|
||||
r: nilOptsConfig.Runtimes["runcv2"],
|
||||
c: nilOptsConfig,
|
||||
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": {
|
||||
r: nonNilOptsConfig.Runtimes["runcv2"],
|
||||
c: nonNilOptsConfig,
|
||||
@ -293,14 +263,6 @@ systemd_cgroup = 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) {
|
||||
opts, err := generateRuntimeOptions(test.r, test.c)
|
||||
|
@ -212,7 +212,7 @@ func (c *Controller) Start(ctx context.Context, id string) (cin sandbox.Controll
|
||||
// Create sandbox task in containerd.
|
||||
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 != "" {
|
||||
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
|
||||
}
|
||||
|
@ -22,9 +22,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
@ -325,19 +323,3 @@ func (c *Controller) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -19,7 +19,6 @@
|
||||
package podsandbox
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/pkg/cri/annotations"
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// taskOpts generates task options for a (sandbox) container.
|
||||
func (c *Controller) taskOpts(runtimeType string) []containerd.NewTaskOpts {
|
||||
return []containerd.NewTaskOpts{}
|
||||
}
|
||||
|
@ -26,8 +26,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
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"
|
||||
)
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// No task options needed for windows.
|
||||
func (c *Controller) taskOpts(runtimeType string) []containerd.NewTaskOpts {
|
||||
return nil
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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{}
|
||||
}
|
@ -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
|
||||
}
|
@ -110,17 +110,12 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get sandbox runtime: %w", err)
|
||||
}
|
||||
|
||||
taskOpts := c.taskOpts(ctrInfo.Runtime.Name)
|
||||
var taskOpts []containerd.NewTaskOpts
|
||||
if ociRuntime.Path != "" {
|
||||
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ import (
|
||||
runtimeoptions "github.com/containerd/containerd/pkg/runtimeoptions/v1"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
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.
|
||||
func generateRuntimeOptions(r criconfig.Runtime, c criconfig.Config) (interface{}, error) {
|
||||
if r.Options == nil {
|
||||
if r.Type != plugin.RuntimeLinuxV1 {
|
||||
return nil, nil
|
||||
}
|
||||
// This is a legacy config, generate runctypes.RuncOptions.
|
||||
return &runctypes.RuncOptions{
|
||||
Runtime: r.Engine,
|
||||
RuntimeRoot: r.Root,
|
||||
SystemdCgroup: c.SystemdCgroup,
|
||||
}, nil
|
||||
return nil, nil
|
||||
}
|
||||
optionsTree, err := toml.TreeFromMap(r.Options)
|
||||
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.
|
||||
func getRuntimeOptionsType(t string) interface{} {
|
||||
switch t {
|
||||
case plugin.RuntimeRuncV1:
|
||||
fallthrough
|
||||
case plugin.RuntimeRuncV2:
|
||||
return &runcoptions.Options{}
|
||||
case plugin.RuntimeLinuxV1:
|
||||
return &runctypes.RuncOptions{}
|
||||
case runtimeRunhcsV1:
|
||||
return &runhcsoptions.Options{}
|
||||
default:
|
||||
|
@ -33,7 +33,6 @@ import (
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/protobuf/types"
|
||||
"github.com/containerd/containerd/reference/docker"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
|
||||
@ -206,29 +205,19 @@ func TestLocalResolve(t *testing.T) {
|
||||
|
||||
func TestGenerateRuntimeOptions(t *testing.T) {
|
||||
nilOpts := `
|
||||
systemd_cgroup = true
|
||||
[containerd]
|
||||
no_pivot = true
|
||||
default_runtime_name = "default"
|
||||
[containerd.runtimes.legacy]
|
||||
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
|
||||
[containerd.runtimes.runc]
|
||||
runtime_type = "` + plugin.RuntimeRuncV1 + `"
|
||||
[containerd.runtimes.runcv2]
|
||||
runtime_type = "` + plugin.RuntimeRuncV2 + `"
|
||||
`
|
||||
nonNilOpts := `
|
||||
systemd_cgroup = true
|
||||
[containerd]
|
||||
no_pivot = true
|
||||
default_runtime_name = "default"
|
||||
[containerd.runtimes.legacy]
|
||||
runtime_type = "` + plugin.RuntimeLinuxV1 + `"
|
||||
[containerd.runtimes.legacy.options]
|
||||
Runtime = "legacy"
|
||||
RuntimeRoot = "/legacy"
|
||||
[containerd.runtimes.runc]
|
||||
runtime_type = "` + plugin.RuntimeRuncV1 + `"
|
||||
[containerd.runtimes.runc.options]
|
||||
BinaryName = "runc"
|
||||
Root = "/runc"
|
||||
@ -245,7 +234,7 @@ systemd_cgroup = true
|
||||
require.NoError(t, err)
|
||||
err = tree.Unmarshal(&nilOptsConfig)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, nilOptsConfig.Runtimes, 3)
|
||||
require.Len(t, nilOptsConfig.Runtimes, 1)
|
||||
|
||||
tree, err = toml.Load(nonNilOpts)
|
||||
require.NoError(t, err)
|
||||
@ -258,32 +247,11 @@ systemd_cgroup = true
|
||||
c criconfig.Config
|
||||
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": {
|
||||
r: nilOptsConfig.Runtimes["runcv2"],
|
||||
c: nilOptsConfig,
|
||||
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": {
|
||||
r: nonNilOptsConfig.Runtimes["runcv2"],
|
||||
c: nonNilOptsConfig,
|
||||
@ -293,14 +261,6 @@ systemd_cgroup = 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) {
|
||||
opts, err := generateRuntimeOptions(test.r, test.c)
|
||||
|
@ -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).",
|
||||
id, name)
|
||||
|
||||
taskOpts := c.taskOpts(ociRuntime.Type)
|
||||
var taskOpts []containerd.NewTaskOpts
|
||||
if ociRuntime.Path != "" {
|
||||
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
|
||||
}
|
||||
|
@ -22,9 +22,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
@ -344,22 +342,6 @@ func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
|
||||
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) {
|
||||
for i := range spec.Linux.Namespaces {
|
||||
if spec.Linux.Namespaces[i].Type == runtimespec.NetworkNamespace {
|
||||
|
@ -19,7 +19,6 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/pkg/cri/annotations"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
@ -51,11 +50,6 @@ func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
|
||||
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) {
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
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"
|
||||
)
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/containerd/containerd"
|
||||
"github.com/containerd/containerd/oci"
|
||||
"github.com/containerd/containerd/snapshots"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
@ -104,11 +103,6 @@ func (c *criService) cleanupSandboxFiles(id string, config *runtime.PodSandboxCo
|
||||
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) {
|
||||
spec.Windows.Network.NetworkNamespace = nsPath
|
||||
}
|
||||
|
@ -93,10 +93,6 @@ 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 = "io.containerd.runc.v2"
|
||||
)
|
||||
|
@ -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"
|
||||
}
|
@ -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
|
@ -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"
|
||||
}
|
@ -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
|
||||
}
|
@ -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;
|
||||
}
|
@ -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)
|
||||
}
|
@ -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")
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
@ -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;
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -23,7 +23,6 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
api "github.com/containerd/containerd/api/services/tasks/v1"
|
||||
@ -47,7 +46,6 @@ import (
|
||||
"github.com/containerd/containerd/protobuf/proto"
|
||||
ptypes "github.com/containerd/containerd/protobuf/types"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/containerd/services"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
@ -89,10 +87,6 @@ func init() {
|
||||
|
||||
func initFunc(ic *plugin.InitContext) (interface{}, error) {
|
||||
config := ic.Config.(*Config)
|
||||
runtimes, err := loadV1Runtimes(ic)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v2r, err := ic.GetByID(plugin.RuntimePluginV2, "task")
|
||||
if err != nil {
|
||||
@ -119,22 +113,13 @@ func initFunc(ic *plugin.InitContext) (interface{}, error) {
|
||||
|
||||
db := m.(*metadata.DB)
|
||||
l := &local{
|
||||
runtimes: runtimes,
|
||||
containers: metadata.NewContainerStore(db),
|
||||
store: db.ContentStore(),
|
||||
publisher: ep.(events.Publisher),
|
||||
monitor: monitor.(runtime.TaskMonitor),
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -154,7 +139,6 @@ func initFunc(ic *plugin.InitContext) (interface{}, error) {
|
||||
}
|
||||
|
||||
type local struct {
|
||||
runtimes map[string]runtime.PlatformRuntime
|
||||
containers containers.Store
|
||||
store content.Store
|
||||
publisher events.Publisher
|
||||
@ -221,15 +205,9 @@ func (l *local) Create(ctx context.Context, r *api.CreateTaskRequest, _ ...grpc.
|
||||
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")
|
||||
} 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
|
||||
}
|
||||
|
||||
rtime := l.v2Runtime
|
||||
|
||||
_, err = rtime.Get(ctx, r.ContainerID)
|
||||
if err != nil && !errdefs.IsNotFound(err) {
|
||||
return nil, errdefs.ToGRPC(err)
|
||||
@ -284,14 +262,8 @@ func (l *local) Delete(ctx context.Context, r *api.DeleteTaskRequest, _ ...grpc.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find runtime manager
|
||||
rtime, err := l.getRuntime(container.Runtime.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get task object
|
||||
t, err := rtime.Get(ctx, container.ID)
|
||||
t, err := l.v2Runtime.Get(ctx, container.ID)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
exit, err := rtime.Delete(ctx, r.ContainerID)
|
||||
exit, err := l.v2Runtime.Delete(ctx, r.ContainerID)
|
||||
if err != nil {
|
||||
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) {
|
||||
resp := &api.ListTasksResponse{}
|
||||
for _, r := range l.allRuntimes() {
|
||||
tasks, err := r.Tasks(ctx, false)
|
||||
if err != nil {
|
||||
return nil, errdefs.ToGRPC(err)
|
||||
}
|
||||
addTasks(ctx, resp, tasks)
|
||||
tasks, err := l.v2Runtime.Tasks(ctx, false)
|
||||
if err != nil {
|
||||
return nil, errdefs.ToGRPC(err)
|
||||
}
|
||||
addTasks(ctx, resp, tasks)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@ -623,13 +593,11 @@ func (l *local) Metrics(ctx context.Context, r *api.MetricsRequest, _ ...grpc.Ca
|
||||
return nil, err
|
||||
}
|
||||
var resp api.MetricsResponse
|
||||
for _, r := range l.allRuntimes() {
|
||||
tasks, err := r.Tasks(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getTasksMetrics(ctx, filter, tasks, &resp)
|
||||
tasks, err := l.v2Runtime.Tasks(ctx, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
getTasksMetrics(ctx, filter, tasks, &resp)
|
||||
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) {
|
||||
runtime, err := l.getRuntime(container.Runtime.Name)
|
||||
if err != nil {
|
||||
return nil, errdefs.ToGRPCf(err, "runtime for task %s", container.Runtime.Name)
|
||||
}
|
||||
t, err := runtime.Get(ctx, container.ID)
|
||||
t, err := l.v2Runtime.Get(ctx, container.ID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "task %v not found", container.ID)
|
||||
}
|
||||
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
|
||||
func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) {
|
||||
if option == nil {
|
||||
@ -760,29 +707,15 @@ func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) {
|
||||
}
|
||||
|
||||
var checkpointPath string
|
||||
switch {
|
||||
case checkRuntime(runtime, "io.containerd.runc"):
|
||||
v, err := typeurl.UnmarshalAny(option)
|
||||
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
|
||||
v, err := typeurl.UnmarshalAny(option)
|
||||
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
|
||||
|
||||
return checkpointPath, nil
|
||||
}
|
||||
@ -794,45 +727,15 @@ func getRestorePath(runtime string, option *ptypes.Any) (string, error) {
|
||||
}
|
||||
|
||||
var restorePath string
|
||||
switch {
|
||||
case checkRuntime(runtime, "io.containerd.runc"):
|
||||
v, err := typeurl.UnmarshalAny(option)
|
||||
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
|
||||
v, err := typeurl.UnmarshalAny(option)
|
||||
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
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ package tasks
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
)
|
||||
|
||||
var tasksServiceRequires = []plugin.Type{
|
||||
@ -28,8 +27,3 @@ var tasksServiceRequires = []plugin.Type{
|
||||
plugin.MetadataPlugin,
|
||||
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
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package tasks
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
)
|
||||
|
||||
var tasksServiceRequires = []plugin.Type{
|
||||
@ -27,8 +26,3 @@ var tasksServiceRequires = []plugin.Type{
|
||||
plugin.MetadataPlugin,
|
||||
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
|
||||
}
|
||||
|
@ -19,11 +19,7 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/containerd/containerd/log"
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
)
|
||||
|
||||
var tasksServiceRequires = []plugin.Type{
|
||||
@ -33,26 +29,3 @@ var tasksServiceRequires = []plugin.Type{
|
||||
plugin.MetadataPlugin,
|
||||
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
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package tasks
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/plugin"
|
||||
"github.com/containerd/containerd/runtime"
|
||||
)
|
||||
|
||||
var tasksServiceRequires = []plugin.Type{
|
||||
@ -27,8 +26,3 @@ var tasksServiceRequires = []plugin.Type{
|
||||
plugin.MetadataPlugin,
|
||||
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
|
||||
}
|
||||
|
8
task.go
8
task.go
@ -41,7 +41,6 @@ import (
|
||||
"github.com/containerd/containerd/protobuf"
|
||||
google_protobuf "github.com/containerd/containerd/protobuf/types"
|
||||
"github.com/containerd/containerd/rootfs"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
"github.com/containerd/typeurl/v2"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
@ -691,15 +690,10 @@ func isCheckpointPathExist(runtime string, v interface{}) bool {
|
||||
}
|
||||
|
||||
switch runtime {
|
||||
case plugin.RuntimeRuncV1, plugin.RuntimeRuncV2:
|
||||
case plugin.RuntimeRuncV2:
|
||||
if opts, ok := v.(*options.CheckpointOptions); ok && opts.ImagePath != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
case plugin.RuntimeLinuxV1:
|
||||
if opts, ok := v.(*runctypes.CheckpointOptions); ok && opts.ImagePath != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
|
51
task_opts.go
51
task_opts.go
@ -28,7 +28,6 @@ import (
|
||||
"github.com/containerd/containerd/errdefs"
|
||||
"github.com/containerd/containerd/images"
|
||||
"github.com/containerd/containerd/mount"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
imagespec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
@ -104,25 +103,14 @@ func WithCheckpointName(name string) CheckpointTaskOpts {
|
||||
// WithCheckpointImagePath sets image path for checkpoint option
|
||||
func WithCheckpointImagePath(path string) CheckpointTaskOpts {
|
||||
return func(r *CheckpointTaskInfo) error {
|
||||
if CheckRuntime(r.Runtime(), "io.containerd.runc") {
|
||||
if r.Options == nil {
|
||||
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
|
||||
if r.Options == nil {
|
||||
r.Options = &options.CheckpointOptions{}
|
||||
}
|
||||
opts, ok := r.Options.(*options.CheckpointOptions)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim checkpoint options format")
|
||||
}
|
||||
opts.ImagePath = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -130,25 +118,14 @@ func WithCheckpointImagePath(path string) CheckpointTaskOpts {
|
||||
// WithRestoreImagePath sets image path for create option
|
||||
func WithRestoreImagePath(path string) NewTaskOpts {
|
||||
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||
if ti.Options == nil {
|
||||
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
|
||||
if ti.Options == nil {
|
||||
ti.Options = &options.Options{}
|
||||
}
|
||||
opts, ok := ti.Options.(*options.Options)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim create options format")
|
||||
}
|
||||
opts.CriuImagePath = path
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -22,84 +22,47 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
"github.com/containerd/containerd/runtime/v2/runc/options"
|
||||
)
|
||||
|
||||
// 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
|
||||
func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||
if ti.Options == nil {
|
||||
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
|
||||
if ti.Options == nil {
|
||||
ti.Options = &options.Options{}
|
||||
}
|
||||
opts, ok := ti.Options.(*options.Options)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim create options format")
|
||||
}
|
||||
opts.NoNewKeyring = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithNoPivotRoot instructs the runtime not to you pivot_root
|
||||
func WithNoPivotRoot(_ context.Context, _ *Client, ti *TaskInfo) error {
|
||||
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||
if ti.Options == nil {
|
||||
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
|
||||
if ti.Options == nil {
|
||||
ti.Options = &options.Options{}
|
||||
}
|
||||
opts, ok := ti.Options.(*options.Options)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim create options format")
|
||||
}
|
||||
opts.NoPivotRoot = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithShimCgroup sets the existing cgroup for the shim
|
||||
func WithShimCgroup(path string) NewTaskOpts {
|
||||
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||
if ti.Options == nil {
|
||||
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
|
||||
if ti.Options == nil {
|
||||
ti.Options = &options.Options{}
|
||||
}
|
||||
opts, ok := ti.Options.(*options.Options)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim create options format")
|
||||
}
|
||||
opts.ShimCgroup = path
|
||||
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
|
||||
func WithUIDOwner(uid uint32) NewTaskOpts {
|
||||
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||
if ti.Options == nil {
|
||||
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
|
||||
if ti.Options == nil {
|
||||
ti.Options = &options.Options{}
|
||||
}
|
||||
opts, ok := ti.Options.(*options.Options)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim create options format")
|
||||
}
|
||||
opts.IoUid = uid
|
||||
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
|
||||
func WithGIDOwner(gid uint32) NewTaskOpts {
|
||||
return func(ctx context.Context, c *Client, ti *TaskInfo) error {
|
||||
if CheckRuntime(ti.Runtime(), "io.containerd.runc") {
|
||||
if ti.Options == nil {
|
||||
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
|
||||
if ti.Options == nil {
|
||||
ti.Options = &options.Options{}
|
||||
}
|
||||
opts, ok := ti.Options.(*options.Options)
|
||||
if !ok {
|
||||
return errors.New("invalid v2 shim create options format")
|
||||
}
|
||||
opts.IoGid = gid
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user