Merge pull request #8262 from mxpv/v1
🪦 Remove `io.containerd.runtime.v1.linux` and `io.containerd.runc.v1`
			
			
This commit is contained in:
		
							
								
								
									
										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,8 +84,6 @@ 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{} | ||||
| 		} | ||||
| @@ -102,22 +98,7 @@ func withCheckpointOpts(rt string, context *cli.Context) containerd.CheckpointTa | ||||
| 		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 | ||||
| 			} | ||||
| 		} | ||||
| 		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. | ||||
|   | ||||
| @@ -19,7 +19,6 @@ 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/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)) | ||||
| 	} | ||||
|  | ||||
| 	statusC, err := task.Wait(ctx) | ||||
| 	if err != nil { | ||||
| @@ -402,20 +394,10 @@ 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") | ||||
| 	} | ||||
| } | ||||
| } | ||||
|  | ||||
| func writeToFile(t *testing.T, filePath, message string) { | ||||
| 	writer, err := os.OpenFile(filePath, os.O_WRONLY, 0600) | ||||
| @@ -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,18 +1115,10 @@ 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{ | ||||
| 	copts := &options.Options{ | ||||
| 		IoUid: 1000, | ||||
| 		IoGid: 2000, | ||||
| 	} | ||||
| 	} else { | ||||
| 		copts = &runctypes.CreateOptions{ | ||||
| 			IoUid: 1000, | ||||
| 			IoGid: 2000, | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	task, err := container.NewTask(ctx, cio.NewCreator(cio.WithStdio), func(_ context.Context, client *Client, r *TaskInfo) error { | ||||
| 		r.Options = copts | ||||
|   | ||||
| @@ -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,16 +329,8 @@ 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 | ||||
| 	} | ||||
| 	optionsTree, err := toml.TreeFromMap(r.Options) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @@ -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,16 +337,8 @@ 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 | ||||
| 	} | ||||
| 	optionsTree, err := toml.TreeFromMap(r.Options) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @@ -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) | ||||
| 	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) | ||||
| 	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,8 +707,6 @@ 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 | ||||
| @@ -772,18 +717,6 @@ func getCheckpointPath(runtime string, option *ptypes.Any) (string, error) { | ||||
| 	} | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	return checkpointPath, nil | ||||
| } | ||||
|  | ||||
| @@ -794,8 +727,6 @@ 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 | ||||
| @@ -805,34 +736,6 @@ func getRestorePath(runtime string, option *ptypes.Any) (string, error) { | ||||
| 		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 | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
|   | ||||
							
								
								
									
										23
									
								
								task_opts.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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,7 +103,6 @@ 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{} | ||||
| 		} | ||||
| @@ -113,16 +111,6 @@ func WithCheckpointImagePath(path string) CheckpointTaskOpts { | ||||
| 			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 | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| @@ -130,7 +118,6 @@ 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{} | ||||
| 		} | ||||
| @@ -139,16 +126,6 @@ func WithRestoreImagePath(path string) NewTaskOpts { | ||||
| 			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 | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -22,14 +22,12 @@ 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{} | ||||
| 	} | ||||
| @@ -38,22 +36,11 @@ func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { | ||||
| 		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 | ||||
| 	} | ||||
| 	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{} | ||||
| 	} | ||||
| @@ -62,26 +49,12 @@ func WithNoPivotRoot(_ context.Context, _ *Client, ti *TaskInfo) error { | ||||
| 		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 | ||||
| 	} | ||||
| 	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{} | ||||
| 		} | ||||
| @@ -90,16 +63,6 @@ func WithShimCgroup(path string) NewTaskOpts { | ||||
| 			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 | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| @@ -107,7 +70,6 @@ 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{} | ||||
| 		} | ||||
| @@ -116,16 +78,6 @@ func WithUIDOwner(uid uint32) NewTaskOpts { | ||||
| 			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 | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| @@ -133,7 +85,6 @@ 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{} | ||||
| 		} | ||||
| @@ -142,16 +93,6 @@ func WithGIDOwner(gid uint32) NewTaskOpts { | ||||
| 			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 | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Akihiro Suda
					Akihiro Suda