mirror of
https://github.com/hanzoai/visor.git
synced 2026-09-24 17:04:14 +00:00
Delete shim v1
gvisor-containerd-shim is not compatible with containerd 1.1 or earlier. Starting from containerd 1.2, shim v2 is the preferred interface. PiperOrigin-RevId: 351485556
This commit is contained in:
committed by
gVisor bot
parent
72ef787749
commit
29ebab9ef0
@@ -116,8 +116,7 @@ go_path(
|
||||
# binaries have been factored into a cli package, which is
|
||||
# a good practice in any case.
|
||||
"//runsc/cli",
|
||||
"//shim/v1/cli",
|
||||
"//shim/v2/cli",
|
||||
"//shim/cli",
|
||||
"//webhook/pkg/cli",
|
||||
|
||||
# Packages that are not dependencies of the above.
|
||||
|
||||
@@ -298,13 +298,9 @@ containerd-test-%: load-basic_alpine load-basic_python load-basic_busybox load-b
|
||||
@$(call sudo,tools/installers:shim)
|
||||
@$(call sudo,test/root:root_test,--runtime=$(RUNTIME) -test.v)
|
||||
|
||||
# Note that we can't run containerd-test-1.1.8 tests here.
|
||||
#
|
||||
# Containerd 1.1.8 should work, but because of a bug in loading images locally
|
||||
# (https://github.com/kubernetes-sigs/cri-tools/issues/421), we are unable to
|
||||
# actually drive the tests. The v1 API is tested exclusively through 1.2.13.
|
||||
# The shim builds with containerd 1.3.9 and it's not backward compatible. Test
|
||||
# with 1.3.9 and newer versions.
|
||||
containerd-tests: ## Runs all supported containerd version tests.
|
||||
containerd-tests: containerd-test-1.2.13
|
||||
containerd-tests: containerd-test-1.3.9
|
||||
containerd-tests: containerd-test-1.4.3
|
||||
|
||||
@@ -433,8 +429,7 @@ $(RELEASE_KEY):
|
||||
$(RELEASE_ARTIFACTS)/%:
|
||||
@mkdir -p $@
|
||||
@$(call copy,//runsc:runsc,$@)
|
||||
@$(call copy,//shim/v1:gvisor-containerd-shim,$@)
|
||||
@$(call copy,//shim/v2:containerd-shim-runsc-v1,$@)
|
||||
@$(call copy,//shim:containerd-shim-runsc-v1,$@)
|
||||
@$(call copy,//debian:debian,$@)
|
||||
|
||||
release: $(RELEASE_KEY) $(RELEASE_ARTIFACTS)/$(ARCH)
|
||||
|
||||
@@ -93,11 +93,10 @@ GO111MODULE=on go get gvisor.dev/gvisor/runsc@go
|
||||
CGO_ENABLED=0 GO111MODULE=on sudo -E go build -o /usr/local/bin/runsc gvisor.dev/gvisor/runsc
|
||||
```
|
||||
|
||||
Subsequently, you can build and install the shim binaries for `containerd`:
|
||||
Subsequently, you can build and install the shim binary for `containerd`:
|
||||
|
||||
```sh
|
||||
GO111MODULE=on sudo -E go build -o /usr/local/bin/gvisor-containerd-shim gvisor.dev/gvisor/shim/v1
|
||||
GO111MODULE=on sudo -E go build -o /usr/local/bin/containerd-shim-runsc-v1 gvisor.dev/gvisor/shim/v2
|
||||
GO111MODULE=on sudo -E go build -o /usr/local/bin/containerd-shim-runsc-v1 gvisor.dev/gvisor/shim
|
||||
```
|
||||
|
||||
Note that this branch is supported in a best effort capacity, and direct
|
||||
|
||||
Vendored
+1
-2
@@ -6,8 +6,7 @@ pkg_tar(
|
||||
name = "debian-bin",
|
||||
srcs = [
|
||||
"//runsc",
|
||||
"//shim/v1:gvisor-containerd-shim",
|
||||
"//shim/v2:containerd-shim-runsc-v1",
|
||||
"//shim:containerd-shim-runsc-v1",
|
||||
],
|
||||
mode = "0755",
|
||||
package_dir = "/usr/bin",
|
||||
|
||||
@@ -27,7 +27,7 @@ doc(
|
||||
name = "containerd_11",
|
||||
src = "containerd_11.md",
|
||||
category = "User Guide",
|
||||
include_in_menu = False,
|
||||
permalink = "/docs/user_guide/containerd/containerd_11/",
|
||||
subcategory = "Containerd",
|
||||
weight = "99",
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ option = "value"
|
||||
```
|
||||
|
||||
The set of options that can be configured can be found in
|
||||
[options.go](https://github.com/google/gvisor/blob/master/pkg/shim/v2/options.go).
|
||||
[options.go](https://cs.opensource.google/gvisor/gvisor/+/master:pkg/shim/options.go).
|
||||
Values under `[runsc_config]` can be used to set arbitrary flags to runsc.
|
||||
`flag = "value"` is converted to `--flag="value"` when runsc is invoked. Run
|
||||
`runsc flags` so see which flags are available
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Older Versions (containerd 1.1)
|
||||
|
||||
**WARNING: containerd 1.1 and shim v1 is no longer supported. The instructions
|
||||
below is kept just for reference in case you're dealing with an old version.
|
||||
It's highly recommended upgrading to the latest version.**
|
||||
|
||||
This document describes how to install and run the `gvisor-containerd-shim`
|
||||
using the untrusted workload CRI extension. This requires `containerd` 1.1 or
|
||||
later.
|
||||
|
||||
@@ -15,14 +15,12 @@ To download and install the latest release manually follow these steps:
|
||||
ARCH=$(uname -m)
|
||||
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
|
||||
wget ${URL}/runsc ${URL}/runsc.sha512 \
|
||||
${URL}/gvisor-containerd-shim ${URL}/gvisor-containerd-shim.sha512 \
|
||||
${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512
|
||||
sha512sum -c runsc.sha512 \
|
||||
-c gvisor-containerd-shim.sha512 \
|
||||
-c containerd-shim-runsc-v1.sha512
|
||||
rm -f *.sha512
|
||||
chmod a+rx runsc gvisor-containerd-shim containerd-shim-runsc-v1
|
||||
sudo mv runsc gvisor-containerd-shim containerd-shim-runsc-v1 /usr/local/bin
|
||||
chmod a+rx runsc containerd-shim-runsc-v1
|
||||
sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ require (
|
||||
github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a // indirect
|
||||
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00 // indirect
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 // indirect
|
||||
github.com/containerd/ttrpc v0.0.0-20200121165050-0be804eadb15 // indirect
|
||||
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737 // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.0.0 // indirect
|
||||
|
||||
@@ -66,9 +66,6 @@ github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZ
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 h1:PRTagVMbJcCezLcHXe8UJvR1oBzp2lG3CEumeFOLOds=
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/ttrpc v0.0.0-20200121165050-0be804eadb15 h1:+jgiLE5QylzgADj0Yldb4id1NQNRrDOROj7KDvY9PEc=
|
||||
github.com/containerd/ttrpc v0.0.0-20200121165050-0be804eadb15/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737 h1:HovfQDS/K3Mr7eyS0QJLxE1CbVUhjZCl6g3OhFJgP1o=
|
||||
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||
|
||||
@@ -57,7 +57,7 @@ global:
|
||||
- "should not use underscores in Go names"
|
||||
exclude:
|
||||
# Generated: exempt all.
|
||||
- pkg/shim/v2/runtimeoptions/runtimeoptions_cri.go
|
||||
- pkg/shim/runtimeoptions/runtimeoptions_cri.go
|
||||
analyzers:
|
||||
asmdecl:
|
||||
external: # Enabled.
|
||||
|
||||
@@ -3,7 +3,7 @@ load("//tools:defs.bzl", "go_library")
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "v2",
|
||||
name = "shim",
|
||||
srcs = [
|
||||
"api.go",
|
||||
"epoll.go",
|
||||
@@ -15,10 +15,10 @@ go_library(
|
||||
visibility = ["//shim:__subpackages__"],
|
||||
deps = [
|
||||
"//pkg/cleanup",
|
||||
"//pkg/shim/proc",
|
||||
"//pkg/shim/runsc",
|
||||
"//pkg/shim/v1/proc",
|
||||
"//pkg/shim/v1/utils",
|
||||
"//pkg/shim/v2/runtimeoptions",
|
||||
"//pkg/shim/runtimeoptions",
|
||||
"//pkg/shim/utils",
|
||||
"//runsc/specutils",
|
||||
"@com_github_burntsushi_toml//:go_default_library",
|
||||
"@com_github_containerd_cgroups//:go_default_library",
|
||||
@@ -13,7 +13,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package v2
|
||||
package shim
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/api/events"
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
// +build linux
|
||||
|
||||
package v2
|
||||
package shim
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package v2
|
||||
package shim
|
||||
|
||||
const optionsType = "io.containerd.runsc.v1.options"
|
||||
|
||||
@@ -11,7 +11,7 @@ go_library(
|
||||
"init.go",
|
||||
"init_state.go",
|
||||
"io.go",
|
||||
"process.go",
|
||||
"proc.go",
|
||||
"types.go",
|
||||
"utils.go",
|
||||
],
|
||||
@@ -39,9 +39,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/shim/runsc"
|
||||
)
|
||||
|
||||
// InitPidFile name of the file that contains the init pid.
|
||||
const InitPidFile = "init.pid"
|
||||
|
||||
// Init represents an initial process for a container.
|
||||
type Init struct {
|
||||
wg sync.WaitGroup
|
||||
@@ -122,7 +119,8 @@ func (p *Init) Create(ctx context.Context, r *CreateConfig) (err error) {
|
||||
return fmt.Errorf("failed to create OCI runtime io pipes: %w", err)
|
||||
}
|
||||
}
|
||||
pidFile := filepath.Join(p.Bundle, InitPidFile)
|
||||
// pidFile is the file that will contain the sandbox pid.
|
||||
pidFile := filepath.Join(p.Bundle, "init.pid")
|
||||
opts := &runsc.CreateOpts{
|
||||
PidFile: pidFile,
|
||||
}
|
||||
@@ -13,7 +13,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package proc contains process-related utilities.
|
||||
// Package proc is responsible to manage the communication between the shim and
|
||||
// the sandbox process running the container.
|
||||
package proc
|
||||
|
||||
import (
|
||||
@@ -15,7 +15,7 @@ go_library(
|
||||
"runtimeoptions.go",
|
||||
"runtimeoptions_cri.go",
|
||||
],
|
||||
visibility = ["//pkg/shim/v2:__pkg__"],
|
||||
visibility = ["//pkg/shim:__pkg__"],
|
||||
deps = ["@com_github_gogo_protobuf//proto:go_default_library"],
|
||||
)
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package v2 implements Containerd Shim v2 interface.
|
||||
package v2
|
||||
// Package shim implements Containerd Shim v2 interface.
|
||||
package shim
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -49,10 +49,10 @@ import (
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/cleanup"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/proc"
|
||||
"gvisor.dev/gvisor/pkg/shim/runsc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/proc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/utils"
|
||||
"gvisor.dev/gvisor/pkg/shim/v2/runtimeoptions"
|
||||
"gvisor.dev/gvisor/pkg/shim/runtimeoptions"
|
||||
"gvisor.dev/gvisor/pkg/shim/utils"
|
||||
"gvisor.dev/gvisor/runsc/specutils"
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
// +build linux
|
||||
|
||||
package v2
|
||||
package shim
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package v2
|
||||
package shim
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package utils contains utility functions.
|
||||
// Package utils container miscellaneous utility function used by the shim.
|
||||
package utils
|
||||
|
||||
import (
|
||||
@@ -1,41 +0,0 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "shim",
|
||||
srcs = [
|
||||
"api.go",
|
||||
"platform.go",
|
||||
"service.go",
|
||||
"shim.go",
|
||||
],
|
||||
visibility = [
|
||||
"//pkg/shim:__subpackages__",
|
||||
"//shim:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/shim/runsc",
|
||||
"//pkg/shim/v1/proc",
|
||||
"//pkg/shim/v1/utils",
|
||||
"@com_github_containerd_console//:go_default_library",
|
||||
"@com_github_containerd_containerd//api/events:go_default_library",
|
||||
"@com_github_containerd_containerd//api/types/task:go_default_library",
|
||||
"@com_github_containerd_containerd//errdefs:go_default_library",
|
||||
"@com_github_containerd_containerd//events:go_default_library",
|
||||
"@com_github_containerd_containerd//log:go_default_library",
|
||||
"@com_github_containerd_containerd//mount:go_default_library",
|
||||
"@com_github_containerd_containerd//namespaces:go_default_library",
|
||||
"@com_github_containerd_containerd//pkg/process:go_default_library",
|
||||
"@com_github_containerd_containerd//pkg/stdio:go_default_library",
|
||||
"@com_github_containerd_containerd//runtime:go_default_library",
|
||||
"@com_github_containerd_containerd//runtime/linux/runctypes:go_default_library",
|
||||
"@com_github_containerd_containerd//runtime/v1/shim/v1:go_default_library",
|
||||
"@com_github_containerd_containerd//sys/reaper:go_default_library",
|
||||
"@com_github_containerd_fifo//:go_default_library",
|
||||
"@com_github_containerd_typeurl//:go_default_library",
|
||||
"@com_github_gogo_protobuf//types:go_default_library",
|
||||
"@org_golang_google_grpc//codes:go_default_library",
|
||||
"@org_golang_google_grpc//status:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2018 The containerd Authors.
|
||||
// Copyright 2019 The gVisor 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
|
||||
//
|
||||
// https://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 (
|
||||
"github.com/containerd/containerd/api/events"
|
||||
)
|
||||
|
||||
// TaskCreate is an alias for events.TaskCreate.
|
||||
type TaskCreate = events.TaskCreate
|
||||
|
||||
// TaskStart is an alias for events.TaskStart.
|
||||
type TaskStart = events.TaskStart
|
||||
|
||||
// TaskOOM is an alias for events.TaskOOM.
|
||||
type TaskOOM = events.TaskOOM
|
||||
|
||||
// TaskExit is an alias for events.TaskExit.
|
||||
type TaskExit = events.TaskExit
|
||||
|
||||
// TaskDelete is an alias for events.TaskDelete.
|
||||
type TaskDelete = events.TaskDelete
|
||||
|
||||
// TaskExecAdded is an alias for events.TaskExecAdded.
|
||||
type TaskExecAdded = events.TaskExecAdded
|
||||
|
||||
// TaskExecStarted is an alias for events.TaskExecStarted.
|
||||
type TaskExecStarted = events.TaskExecStarted
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2018 The containerd Authors.
|
||||
// Copyright 2019 The gVisor 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
|
||||
//
|
||||
// https://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"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"github.com/containerd/fifo"
|
||||
)
|
||||
|
||||
type linuxPlatform struct {
|
||||
epoller *console.Epoller
|
||||
}
|
||||
|
||||
func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg *sync.WaitGroup) (console.Console, error) {
|
||||
if p.epoller == nil {
|
||||
return nil, fmt.Errorf("uninitialized epoller")
|
||||
}
|
||||
|
||||
epollConsole, err := p.epoller.Add(console)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if stdin != "" {
|
||||
in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go func() {
|
||||
p := bufPool.Get().(*[]byte)
|
||||
defer bufPool.Put(p)
|
||||
io.CopyBuffer(epollConsole, in, *p)
|
||||
}()
|
||||
}
|
||||
|
||||
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)
|
||||
go func() {
|
||||
p := bufPool.Get().(*[]byte)
|
||||
defer bufPool.Put(p)
|
||||
io.CopyBuffer(outw, epollConsole, *p)
|
||||
epollConsole.Close()
|
||||
outr.Close()
|
||||
outw.Close()
|
||||
wg.Done()
|
||||
}()
|
||||
return epollConsole, nil
|
||||
}
|
||||
|
||||
func (p *linuxPlatform) ShutdownConsole(ctx context.Context, cons console.Console) error {
|
||||
if p.epoller == nil {
|
||||
return fmt.Errorf("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,572 +0,0 @@
|
||||
// Copyright 2018 The containerd Authors.
|
||||
// Copyright 2019 The gVisor 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
|
||||
//
|
||||
// https://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"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/containerd/console"
|
||||
"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/runtime"
|
||||
"github.com/containerd/containerd/runtime/linux/runctypes"
|
||||
shim "github.com/containerd/containerd/runtime/v1/shim/v1"
|
||||
"github.com/containerd/containerd/sys/reaper"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/gogo/protobuf/types"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/runsc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/proc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
empty = &types.Empty{}
|
||||
bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
buffer := make([]byte, 32<<10)
|
||||
return &buffer
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Config contains shim specific configuration.
|
||||
type Config struct {
|
||||
Path string
|
||||
Namespace string
|
||||
WorkDir string
|
||||
RuntimeRoot string
|
||||
RunscConfig map[string]string
|
||||
}
|
||||
|
||||
// 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)
|
||||
s := &Service{
|
||||
config: config,
|
||||
context: ctx,
|
||||
processes: make(map[string]process.Process),
|
||||
events: make(chan interface{}, 128),
|
||||
ec: proc.ExitCh,
|
||||
}
|
||||
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 proc.Exit
|
||||
|
||||
// Filled by Create()
|
||||
id string
|
||||
bundle string
|
||||
}
|
||||
|
||||
// Create creates a new initial process and container with the underlying OCI runtime.
|
||||
func (s *Service) Create(ctx context.Context, r *shim.CreateTaskRequest) (_ *shim.CreateTaskResponse, err error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
var mounts []proc.Mount
|
||||
for _, m := range r.Rootfs {
|
||||
mounts = append(mounts, proc.Mount{
|
||||
Type: m.Type,
|
||||
Source: m.Source,
|
||||
Target: m.Target,
|
||||
Options: m.Options,
|
||||
})
|
||||
}
|
||||
|
||||
rootfs := filepath.Join(r.Bundle, "rootfs")
|
||||
if err := os.Mkdir(rootfs, 0711); err != nil && !os.IsExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := &proc.CreateConfig{
|
||||
ID: r.ID,
|
||||
Bundle: r.Bundle,
|
||||
Runtime: r.Runtime,
|
||||
Rootfs: mounts,
|
||||
Terminal: r.Terminal,
|
||||
Stdin: r.Stdin,
|
||||
Stdout: r.Stdout,
|
||||
Stderr: r.Stderr,
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if err2 := mount.UnmountAll(rootfs, 0); err2 != nil {
|
||||
log.G(ctx).WithError(err2).Warn("Failed to cleanup rootfs mount")
|
||||
}
|
||||
}
|
||||
}()
|
||||
for _, rm := range mounts {
|
||||
m := &mount.Mount{
|
||||
Type: rm.Type,
|
||||
Source: rm.Source,
|
||||
Options: rm.Options,
|
||||
}
|
||||
if err := m.Mount(rootfs); err != nil {
|
||||
return nil, fmt.Errorf("failed to mount rootfs component %v: %w", m, err)
|
||||
}
|
||||
}
|
||||
process, err := newInit(
|
||||
s.config.Path,
|
||||
s.config.WorkDir,
|
||||
s.config.RuntimeRoot,
|
||||
s.config.Namespace,
|
||||
s.config.RunscConfig,
|
||||
s.platform,
|
||||
config,
|
||||
r.Options,
|
||||
)
|
||||
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 &shim.CreateTaskResponse{
|
||||
Pid: uint32(pid),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start starts a process.
|
||||
func (s *Service) Start(ctx context.Context, r *shim.StartRequest) (*shim.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 &shim.StartResponse{
|
||||
ID: p.ID(),
|
||||
Pid: uint32(p.Pid()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Delete deletes the initial process and container.
|
||||
func (s *Service) Delete(ctx context.Context, r *types.Empty) (*shim.DeleteResponse, error) {
|
||||
p, err := s.getInitProcess()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p.Delete(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.mu.Lock()
|
||||
delete(s.processes, s.id)
|
||||
s.mu.Unlock()
|
||||
s.platform.Close()
|
||||
return &shim.DeleteResponse{
|
||||
ExitStatus: uint32(p.ExitStatus()),
|
||||
ExitedAt: p.ExitedAt(),
|
||||
Pid: uint32(p.Pid()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteProcess deletes an exec'd process.
|
||||
func (s *Service) DeleteProcess(ctx context.Context, r *shim.DeleteProcessRequest) (*shim.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, err
|
||||
}
|
||||
s.mu.Lock()
|
||||
delete(s.processes, r.ID)
|
||||
s.mu.Unlock()
|
||||
return &shim.DeleteResponse{
|
||||
ExitStatus: uint32(p.ExitStatus()),
|
||||
ExitedAt: p.ExitedAt(),
|
||||
Pid: uint32(p.Pid()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Exec spawns an additional process inside the container.
|
||||
func (s *Service) Exec(ctx context.Context, r *shim.ExecProcessRequest) (*types.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.(*proc.Init).Exec(ctx, s.config.Path, &proc.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 resises the terminal of a process.
|
||||
func (s *Service) ResizePty(ctx context.Context, r *shim.ResizePtyRequest) (*types.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),
|
||||
}
|
||||
p, err := s.getExecProcess(r.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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 *shim.StateRequest) (*shim.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.StatusUnknown
|
||||
switch st {
|
||||
case "created":
|
||||
status = task.StatusCreated
|
||||
case "running":
|
||||
status = task.StatusRunning
|
||||
case "stopped":
|
||||
status = task.StatusStopped
|
||||
}
|
||||
sio := p.Stdio()
|
||||
return &shim.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: p.ExitedAt(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Pause pauses the container.
|
||||
func (s *Service) Pause(ctx context.Context, r *types.Empty) (*types.Empty, error) {
|
||||
return empty, errdefs.ToGRPC(errdefs.ErrNotImplemented)
|
||||
}
|
||||
|
||||
// Resume resumes the container.
|
||||
func (s *Service) Resume(ctx context.Context, r *types.Empty) (*types.Empty, error) {
|
||||
return empty, errdefs.ToGRPC(errdefs.ErrNotImplemented)
|
||||
}
|
||||
|
||||
// Kill kills a process with the provided signal.
|
||||
func (s *Service) Kill(ctx context.Context, r *shim.KillRequest) (*types.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 *shim.ListPidsRequest) (*shim.ListPidsResponse, error) {
|
||||
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 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 = a
|
||||
break
|
||||
}
|
||||
}
|
||||
processes = append(processes, &pInfo)
|
||||
}
|
||||
return &shim.ListPidsResponse{
|
||||
Processes: processes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CloseIO closes the I/O context of a process.
|
||||
func (s *Service) CloseIO(ctx context.Context, r *shim.CloseIORequest) (*types.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 checkpoints the container.
|
||||
func (s *Service) Checkpoint(ctx context.Context, r *shim.CheckpointTaskRequest) (*types.Empty, error) {
|
||||
return empty, errdefs.ToGRPC(errdefs.ErrNotImplemented)
|
||||
}
|
||||
|
||||
// ShimInfo returns shim information such as the shim's pid.
|
||||
func (s *Service) ShimInfo(ctx context.Context, r *types.Empty) (*shim.ShimInfoResponse, error) {
|
||||
return &shim.ShimInfoResponse{
|
||||
ShimPid: uint32(os.Getpid()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Update updates a running container.
|
||||
func (s *Service) Update(ctx context.Context, r *shim.UpdateTaskRequest) (*types.Empty, error) {
|
||||
return empty, errdefs.ToGRPC(errdefs.ErrNotImplemented)
|
||||
}
|
||||
|
||||
// Wait waits for a process to exit.
|
||||
func (s *Service) Wait(ctx context.Context, r *shim.WaitRequest) (*shim.WaitResponse, error) {
|
||||
p, err := s.getExecProcess(r.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.Wait()
|
||||
|
||||
return &shim.WaitResponse{
|
||||
ExitStatus: uint32(p.ExitStatus()),
|
||||
ExitedAt: p.ExitedAt(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Service) processExits() {
|
||||
for e := range s.ec {
|
||||
s.checkProcesses(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) allProcesses() []process.Process {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
res := make([]process.Process, 0, len(s.processes))
|
||||
for _, p := range s.processes {
|
||||
res = append(res, p)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *Service) checkProcesses(e proc.Exit) {
|
||||
for _, p := range s.allProcesses() {
|
||||
if p.ID() == e.ID {
|
||||
if ip, ok := p.(*proc.Init); ok {
|
||||
// Ensure all children are killed.
|
||||
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 <- &TaskExit{
|
||||
ContainerID: s.id,
|
||||
ID: p.ID(),
|
||||
Pid: uint32(p.Pid()),
|
||||
ExitStatus: uint32(e.Status),
|
||||
ExitedAt: p.ExitedAt(),
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
|
||||
p, err := s.getInitProcess()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ps, err := p.(*proc.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 the init 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 the given 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 *TaskCreate:
|
||||
return runtime.TaskCreateEventTopic
|
||||
case *TaskStart:
|
||||
return runtime.TaskStartEventTopic
|
||||
case *TaskOOM:
|
||||
return runtime.TaskOOMEventTopic
|
||||
case *TaskExit:
|
||||
return runtime.TaskExitEventTopic
|
||||
case *TaskDelete:
|
||||
return runtime.TaskDeleteEventTopic
|
||||
case *TaskExecAdded:
|
||||
return runtime.TaskExecAddedEventTopic
|
||||
case *TaskExecStarted:
|
||||
return runtime.TaskExecStartedEventTopic
|
||||
default:
|
||||
log.L.Printf("no topic for type %#v", e)
|
||||
}
|
||||
return runtime.TaskUnknownTopic
|
||||
}
|
||||
|
||||
func newInit(path, workDir, runtimeRoot, namespace string, config map[string]string, platform stdio.Platform, r *proc.CreateConfig, options *types.Any) (*proc.Init, error) {
|
||||
var opts runctypes.CreateOptions
|
||||
if options != nil {
|
||||
v, err := typeurl.UnmarshalAny(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts = *v.(*runctypes.CreateOptions)
|
||||
}
|
||||
|
||||
spec, err := utils.ReadSpec(r.Bundle)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read oci spec: %w", err)
|
||||
}
|
||||
if err := utils.UpdateVolumeAnnotations(r.Bundle, spec); err != nil {
|
||||
return nil, fmt.Errorf("update volume annotations: %w", err)
|
||||
}
|
||||
|
||||
runsc.FormatRunscLogPath(r.ID, config)
|
||||
rootfs := filepath.Join(path, "rootfs")
|
||||
runtime := proc.NewRunsc(runtimeRoot, path, namespace, r.Runtime, config)
|
||||
p := proc.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(opts.IoUid)
|
||||
p.IoGID = int(opts.IoGid)
|
||||
p.Sandbox = utils.IsSandbox(spec)
|
||||
p.UserLog = utils.UserLogPath(spec)
|
||||
p.Monitor = reaper.Default
|
||||
return p, nil
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Copyright 2018 The containerd Authors.
|
||||
// Copyright 2019 The gVisor 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
|
||||
//
|
||||
// https://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 contains the core containerd shim implementation.
|
||||
package shim
|
||||
@@ -36,7 +36,6 @@ import (
|
||||
type Crictl struct {
|
||||
logger testutil.Logger
|
||||
endpoint string
|
||||
runpArgs []string
|
||||
cleanup []func()
|
||||
}
|
||||
|
||||
@@ -72,14 +71,13 @@ func ResolvePath(executable string) string {
|
||||
|
||||
// NewCrictl returns a Crictl configured with a timeout and an endpoint over
|
||||
// which it will talk to containerd.
|
||||
func NewCrictl(logger testutil.Logger, endpoint string, runpArgs []string) *Crictl {
|
||||
func NewCrictl(logger testutil.Logger, endpoint string) *Crictl {
|
||||
// Attempt to find the executable, but don't bother propagating the
|
||||
// error at this point. The first command executed will return with a
|
||||
// binary not found error.
|
||||
return &Crictl{
|
||||
logger: logger,
|
||||
endpoint: endpoint,
|
||||
runpArgs: runpArgs,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-1
@@ -1,7 +1,17 @@
|
||||
load("//tools:defs.bzl", "pkg_tar")
|
||||
load("//tools:defs.bzl", "go_binary", "pkg_tar")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_binary(
|
||||
name = "containerd-shim-runsc-v1",
|
||||
srcs = ["main.go"],
|
||||
static = True,
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = ["//shim/cli"],
|
||||
)
|
||||
|
||||
pkg_tar(
|
||||
name = "config",
|
||||
srcs = [
|
||||
|
||||
+3
-5
@@ -1,10 +1,8 @@
|
||||
# Shim Overview
|
||||
|
||||
Integration with containerd is done via a [shim][shims]. There are various shims
|
||||
supported for different versions of [containerd][containerd].
|
||||
|
||||
- [Containerd 1.2+ (shim v2)](https://gvisor.dev/docs/user_guide/containerd/quick_start/)
|
||||
- [Containerd 1.1 (shim v1)](https://gvisor.dev/docs/user_guide/containerd/containerd_11/)
|
||||
Integration with containerd is done via a [shim][shims]. The shim implements
|
||||
[containerd][containerd] shim v2 and is supported with containerd 1.3 and newer.
|
||||
[Here is how to get started](https://gvisor.dev/docs/user_guide/containerd/quick_start/)
|
||||
|
||||
[containerd]: https://github.com/containerd/containerd
|
||||
[shims]: https://iximiuz.com/en/posts/implementing-container-runtime-shim/
|
||||
|
||||
@@ -7,10 +7,10 @@ go_library(
|
||||
srcs = ["cli.go"],
|
||||
visibility = [
|
||||
"//:__pkg__",
|
||||
"//shim/v2:__pkg__",
|
||||
"//shim:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/shim/v2",
|
||||
"//pkg/shim",
|
||||
"@com_github_containerd_containerd//runtime/v2/shim:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -17,12 +17,12 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/containerd/containerd/runtime/v2/shim"
|
||||
containerdshim "github.com/containerd/containerd/runtime/v2/shim"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/v2"
|
||||
"gvisor.dev/gvisor/pkg/shim"
|
||||
)
|
||||
|
||||
// Main is the main entrypoint.
|
||||
func Main() {
|
||||
shim.Run("io.containerd.runsc.v1", v2.New)
|
||||
containerdshim.Run("io.containerd.runsc.v1", shim.New)
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/shim/v2/cli"
|
||||
"gvisor.dev/gvisor/shim/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -1,13 +0,0 @@
|
||||
load("//tools:defs.bzl", "go_binary")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_binary(
|
||||
name = "gvisor-containerd-shim",
|
||||
srcs = ["main.go"],
|
||||
static = True,
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = ["//shim/v1/cli"],
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_library(
|
||||
name = "cli",
|
||||
srcs = [
|
||||
"api.go",
|
||||
"cli.go",
|
||||
"config.go",
|
||||
],
|
||||
visibility = [
|
||||
"//:__pkg__",
|
||||
"//shim/v1:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/shim/runsc",
|
||||
"//pkg/shim/v1/shim",
|
||||
"@com_github_burntsushi_toml//:go_default_library",
|
||||
"@com_github_containerd_containerd//events:go_default_library",
|
||||
"@com_github_containerd_containerd//namespaces:go_default_library",
|
||||
"@com_github_containerd_containerd//runtime/v1/shim/v1:go_default_library",
|
||||
"@com_github_containerd_containerd//sys:go_default_library",
|
||||
"@com_github_containerd_containerd//sys/reaper:go_default_library",
|
||||
"@com_github_containerd_ttrpc//:go_default_library",
|
||||
"@com_github_containerd_typeurl//:go_default_library",
|
||||
"@com_github_gogo_protobuf//types:go_default_library",
|
||||
"@org_golang_x_sys//unix:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2018 The containerd Authors.
|
||||
// Copyright 2019 The gVisor 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
|
||||
//
|
||||
// https://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 cli
|
||||
|
||||
import (
|
||||
shim "github.com/containerd/containerd/runtime/v1/shim/v1"
|
||||
)
|
||||
|
||||
type KillRequest = shim.KillRequest
|
||||
|
||||
var registerShimService = shim.RegisterShimService
|
||||
@@ -1,266 +0,0 @@
|
||||
// Copyright 2018 The containerd Authors.
|
||||
// Copyright 2019 The gVisor 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
|
||||
//
|
||||
// https://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 cli defines the command line interface for the V1 shim.
|
||||
package cli
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containerd/containerd/events"
|
||||
"github.com/containerd/containerd/namespaces"
|
||||
"github.com/containerd/containerd/sys"
|
||||
"github.com/containerd/containerd/sys/reaper"
|
||||
"github.com/containerd/ttrpc"
|
||||
"github.com/containerd/typeurl"
|
||||
"github.com/gogo/protobuf/types"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/shim/runsc"
|
||||
"gvisor.dev/gvisor/pkg/shim/v1/shim"
|
||||
)
|
||||
|
||||
var (
|
||||
debugFlag bool
|
||||
namespaceFlag string
|
||||
socketFlag string
|
||||
addressFlag string
|
||||
workdirFlag string
|
||||
runtimeRootFlag string
|
||||
containerdBinaryFlag string
|
||||
shimConfigFlag string
|
||||
)
|
||||
|
||||
// Containerd defaults to runc, unless another runtime is explicitly specified.
|
||||
// We keep the same default to make the default behavior consistent.
|
||||
const defaultRoot = "/run/containerd/runc"
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&debugFlag, "debug", false, "enable debug output in logs")
|
||||
flag.StringVar(&namespaceFlag, "namespace", "", "namespace that owns the shim")
|
||||
flag.StringVar(&socketFlag, "socket", "", "abstract socket path to serve")
|
||||
flag.StringVar(&addressFlag, "address", "", "grpc address back to main containerd")
|
||||
flag.StringVar(&workdirFlag, "workdir", "", "path used to storge large temporary data")
|
||||
flag.StringVar(&runtimeRootFlag, "runtime-root", defaultRoot, "root directory for the runtime")
|
||||
|
||||
// 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.StringVar(&shimConfigFlag, "config", "/etc/containerd/runsc.toml", "path to the shim configuration file")
|
||||
}
|
||||
|
||||
// Main is the main entrypoint.
|
||||
func Main() {
|
||||
flag.Parse()
|
||||
|
||||
// This is a hack. Exec current process to run standard containerd-shim
|
||||
// if runtime root is not `runsc`. We don't need this for shim v2 api.
|
||||
if filepath.Base(runtimeRootFlag) != "runsc" {
|
||||
if err := executeRuncShim(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gvisor-containerd-shim: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Run regular shim if needed.
|
||||
if err := executeShim(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gvisor-containerd-shim: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// executeRuncShim execs current process to a containerd-shim process and
|
||||
// retains all flags and envs.
|
||||
func executeRuncShim() error {
|
||||
c, err := loadConfig(shimConfigFlag)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to load shim config: %w", err)
|
||||
}
|
||||
shimPath := c.RuncShim
|
||||
if shimPath == "" {
|
||||
shimPath, err = exec.LookPath("containerd-shim")
|
||||
if err != nil {
|
||||
return fmt.Errorf("lookup containerd-shim failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
args := append([]string{shimPath}, os.Args[1:]...)
|
||||
if err := syscall.Exec(shimPath, args, os.Environ()); err != nil {
|
||||
return fmt.Errorf("exec containerd-shim @ %q failed: %w", shimPath, err)
|
||||
}
|
||||
return 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
|
||||
}
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
server, err := ttrpc.NewServer(ttrpc.WithServerHandshaker(ttrpc.UnixSocketRequireSameUser()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed creating server: %w", err)
|
||||
}
|
||||
c, err := loadConfig(shimConfigFlag)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to load shim config: %w", err)
|
||||
}
|
||||
sv, err := shim.NewService(
|
||||
shim.Config{
|
||||
Path: path,
|
||||
Namespace: namespaceFlag,
|
||||
WorkDir: workdirFlag,
|
||||
RuntimeRoot: runtimeRootFlag,
|
||||
RunscConfig: c.RunscConfig,
|
||||
},
|
||||
&remoteEventsPublisher{address: addressFlag},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
registerShimService(server, sv)
|
||||
if err := serve(server, socketFlag); err != nil {
|
||||
return err
|
||||
}
|
||||
return handleSignals(signals, server, sv)
|
||||
}
|
||||
|
||||
// serve serves the ttrpc API over a unix socket at the provided path this
|
||||
// function does not block.
|
||||
func serve(server *ttrpc.Server, path string) error {
|
||||
var (
|
||||
l net.Listener
|
||||
err error
|
||||
)
|
||||
if path == "" {
|
||||
l, err = net.FileListener(os.NewFile(3, "socket"))
|
||||
} else {
|
||||
if len(path) > 106 {
|
||||
return fmt.Errorf("%q: unix socket path too long (> 106)", path)
|
||||
}
|
||||
l, err = net.Listen("unix", "\x00"+path)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
defer l.Close()
|
||||
err := server.Serve(context.Background(), l)
|
||||
if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
|
||||
log.Fatalf("ttrpc server failure: %v", err)
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
// TODO(random-liu): Move shim/reaper.go to a separate package.
|
||||
runsc.Monitor = reaper.Default
|
||||
// Set the shim as the subreaper for all orphaned processes created by
|
||||
// the container.
|
||||
if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return signals, nil
|
||||
}
|
||||
|
||||
func handleSignals(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 := sys.Reap(false); err != nil {
|
||||
log.Printf("reap error: %v", err)
|
||||
}
|
||||
case unix.SIGTERM, unix.SIGINT:
|
||||
go termOnce.Do(func() {
|
||||
ctx := context.TODO()
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
log.Printf("failed to shutdown server: %v", err)
|
||||
}
|
||||
// Ensure our child is dead if any.
|
||||
sv.Kill(ctx, &KillRequest{
|
||||
Signal: uint32(syscall.SIGKILL),
|
||||
All: true,
|
||||
})
|
||||
sv.Delete(context.Background(), &types.Empty{})
|
||||
close(done)
|
||||
})
|
||||
case unix.SIGPIPE:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type remoteEventsPublisher struct {
|
||||
address string
|
||||
}
|
||||
|
||||
func (l *remoteEventsPublisher) Publish(ctx context.Context, topic string, event events.Event) error {
|
||||
ns, _ := namespaces.Namespace(ctx)
|
||||
encoded, err := typeurl.MarshalAny(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := encoded.Marshal()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, containerdBinaryFlag, "--address", l.address, "publish", "--topic", topic, "--namespace", ns)
|
||||
cmd.Stdin = bytes.NewReader(data)
|
||||
c, err := reaper.Default.Start(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
status, err := reaper.Default.Wait(cmd, c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to publish event: %w", err)
|
||||
}
|
||||
if status != 0 {
|
||||
return fmt.Errorf("failed to publish event: status %d", status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2018 The gVisor 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
|
||||
//
|
||||
// https://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 cli
|
||||
|
||||
import "github.com/BurntSushi/toml"
|
||||
|
||||
// config is the configuration for gvisor containerd shim.
|
||||
type config struct {
|
||||
// RuncShim is the shim binary path for standard containerd-shim for runc.
|
||||
// When the runtime is `runc`, gvisor containerd shim will exec current
|
||||
// process to standard containerd-shim. This is a work around for containerd
|
||||
// 1.1. In containerd 1.2, containerd will choose different containerd-shims
|
||||
// based on runtime.
|
||||
RuncShim string `toml:"runc_shim"`
|
||||
// RunscConfig is configuration for runsc. The key value will be converted
|
||||
// to runsc flags --key=value directly.
|
||||
RunscConfig map[string]string `toml:"runsc_config"`
|
||||
}
|
||||
|
||||
// loadConfig load gvisor containerd shim config from config file.
|
||||
func loadConfig(path string) (*config, error) {
|
||||
var c config
|
||||
_, err := toml.DecodeFile(path, &c)
|
||||
if err != nil {
|
||||
return &c, err
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2020 The gVisor 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
// Binary gvisor-containerd-shim is the v1 containerd shim.
|
||||
package main
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/shim/v1/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.Main()
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
load("//tools:defs.bzl", "go_binary")
|
||||
|
||||
package(licenses = ["notice"])
|
||||
|
||||
go_binary(
|
||||
name = "containerd-shim-runsc-v1",
|
||||
srcs = ["main.go"],
|
||||
static = True,
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
],
|
||||
deps = ["//shim/v2/cli"],
|
||||
)
|
||||
+128
-188
@@ -98,29 +98,25 @@ var Httpd = SimpleSpec("httpd", "basic/httpd", nil, nil)
|
||||
|
||||
// TestCrictlSanity refers to b/112433158.
|
||||
func TestCrictlSanity(t *testing.T) {
|
||||
for _, version := range allVersions {
|
||||
t.Run(version, func(t *testing.T) {
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t, version)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/httpd", Sandbox("default"), Httpd)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/httpd", Sandbox("default"), Httpd)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
// Look for the httpd page.
|
||||
if err = httpGet(crictl, podID, "index.html"); err != nil {
|
||||
t.Fatalf("failed to get page: %v", err)
|
||||
}
|
||||
// Look for the httpd page.
|
||||
if err = httpGet(crictl, podID, "index.html"); err != nil {
|
||||
t.Fatalf("failed to get page: %v", err)
|
||||
}
|
||||
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
})
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,152 +150,130 @@ var HttpdMountPaths = SimpleSpec("httpd", "basic/httpd", nil, map[string]interfa
|
||||
|
||||
// TestMountPaths refers to b/117635704.
|
||||
func TestMountPaths(t *testing.T) {
|
||||
for _, version := range allVersions {
|
||||
t.Run(version, func(t *testing.T) {
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t, version)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/httpd", Sandbox("default"), HttpdMountPaths)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/httpd", Sandbox("default"), HttpdMountPaths)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
// Look for the directory available at /test.
|
||||
if err = httpGet(crictl, podID, "test"); err != nil {
|
||||
t.Fatalf("failed to get page: %v", err)
|
||||
}
|
||||
// Look for the directory available at /test.
|
||||
if err = httpGet(crictl, podID, "test"); err != nil {
|
||||
t.Fatalf("failed to get page: %v", err)
|
||||
}
|
||||
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
})
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMountPaths refers to b/118728671.
|
||||
func TestMountOverSymlinks(t *testing.T) {
|
||||
for _, version := range allVersions {
|
||||
t.Run(version, func(t *testing.T) {
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t, version)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
spec := SimpleSpec("busybox", "basic/resolv", []string{"sleep", "1000"}, nil)
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/resolv", Sandbox("default"), spec)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
spec := SimpleSpec("busybox", "basic/resolv", []string{"sleep", "1000"}, nil)
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/resolv", Sandbox("default"), spec)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
out, err := crictl.Exec(contID, "readlink", "/etc/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("readlink failed: %v, out: %s", err, out)
|
||||
}
|
||||
if want := "/tmp/resolv.conf"; !strings.Contains(string(out), want) {
|
||||
t.Fatalf("/etc/resolv.conf is not pointing to %q: %q", want, string(out))
|
||||
}
|
||||
out, err := crictl.Exec(contID, "readlink", "/etc/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("readlink failed: %v, out: %s", err, out)
|
||||
}
|
||||
if want := "/tmp/resolv.conf"; !strings.Contains(string(out), want) {
|
||||
t.Fatalf("/etc/resolv.conf is not pointing to %q: %q", want, string(out))
|
||||
}
|
||||
|
||||
etc, err := crictl.Exec(contID, "cat", "/etc/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("cat failed: %v, out: %s", err, etc)
|
||||
}
|
||||
tmp, err := crictl.Exec(contID, "cat", "/tmp/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("cat failed: %v, out: %s", err, out)
|
||||
}
|
||||
if tmp != etc {
|
||||
t.Fatalf("file content doesn't match:\n\t/etc/resolv.conf: %s\n\t/tmp/resolv.conf: %s", string(etc), string(tmp))
|
||||
}
|
||||
etc, err := crictl.Exec(contID, "cat", "/etc/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("cat failed: %v, out: %s", err, etc)
|
||||
}
|
||||
tmp, err := crictl.Exec(contID, "cat", "/tmp/resolv.conf")
|
||||
if err != nil {
|
||||
t.Fatalf("cat failed: %v, out: %s", err, out)
|
||||
}
|
||||
if tmp != etc {
|
||||
t.Fatalf("file content doesn't match:\n\t/etc/resolv.conf: %s\n\t/tmp/resolv.conf: %s", string(etc), string(tmp))
|
||||
}
|
||||
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
})
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHomeDir tests that the HOME environment variable is set for
|
||||
// Pod containers.
|
||||
func TestHomeDir(t *testing.T) {
|
||||
for _, version := range allVersions {
|
||||
t.Run(version, func(t *testing.T) {
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t, version)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Note that container ID returned here is a sub-container. All Pod
|
||||
// containers are sub-containers. The root container of the sandbox is the
|
||||
// pause container.
|
||||
t.Run("sub-container", func(t *testing.T) {
|
||||
contSpec := SimpleSpec("subcontainer", "basic/busybox", []string{"sh", "-c", "echo $HOME"}, nil)
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/busybox", Sandbox("subcont-sandbox"), contSpec)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
out, err := crictl.Logs(contID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed retrieving container logs: %v, out: %s", err, out)
|
||||
}
|
||||
if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
|
||||
t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
|
||||
}
|
||||
|
||||
// Stop everything; note that the pod may have already stopped.
|
||||
crictl.StopPodAndContainer(podID, contID)
|
||||
})
|
||||
|
||||
// Tests that HOME is set for the exec process.
|
||||
t.Run("exec", func(t *testing.T) {
|
||||
contSpec := SimpleSpec("exec", "basic/busybox", []string{"sleep", "1000"}, nil)
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/busybox", Sandbox("exec-sandbox"), contSpec)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
out, err := crictl.Exec(contID, "sh", "-c", "echo $HOME")
|
||||
if err != nil {
|
||||
t.Fatalf("failed retrieving container logs: %v, out: %s", err, out)
|
||||
}
|
||||
if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
|
||||
t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
|
||||
}
|
||||
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
})
|
||||
})
|
||||
// Setup containerd and crictl.
|
||||
crictl, cleanup, err := setup(t)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to setup crictl: %v", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
// Note that container ID returned here is a sub-container. All Pod
|
||||
// containers are sub-containers. The root container of the sandbox is the
|
||||
// pause container.
|
||||
t.Run("sub-container", func(t *testing.T) {
|
||||
contSpec := SimpleSpec("subcontainer", "basic/busybox", []string{"sh", "-c", "echo $HOME"}, nil)
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/busybox", Sandbox("subcont-sandbox"), contSpec)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
out, err := crictl.Logs(contID)
|
||||
if err != nil {
|
||||
t.Fatalf("failed retrieving container logs: %v, out: %s", err, out)
|
||||
}
|
||||
if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
|
||||
t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
|
||||
}
|
||||
|
||||
// Stop everything; note that the pod may have already stopped.
|
||||
crictl.StopPodAndContainer(podID, contID)
|
||||
})
|
||||
|
||||
// Tests that HOME is set for the exec process.
|
||||
t.Run("exec", func(t *testing.T) {
|
||||
contSpec := SimpleSpec("exec", "basic/busybox", []string{"sleep", "1000"}, nil)
|
||||
podID, contID, err := crictl.StartPodAndContainer(containerdRuntime, "basic/busybox", Sandbox("exec-sandbox"), contSpec)
|
||||
if err != nil {
|
||||
t.Fatalf("start failed: %v", err)
|
||||
}
|
||||
|
||||
out, err := crictl.Exec(contID, "sh", "-c", "echo $HOME")
|
||||
if err != nil {
|
||||
t.Fatalf("failed retrieving container logs: %v, out: %s", err, out)
|
||||
}
|
||||
if got, want := strings.TrimSpace(string(out)), "/root"; got != want {
|
||||
t.Fatalf("Home directory invalid. Got %q, Want : %q", got, want)
|
||||
}
|
||||
|
||||
// Stop everything.
|
||||
if err := crictl.StopPodAndContainer(podID, contID); err != nil {
|
||||
t.Fatalf("stop failed: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const containerdRuntime = "runsc"
|
||||
|
||||
const v1Template = `
|
||||
disabled_plugins = ["restart"]
|
||||
[plugins.cri]
|
||||
disable_tcp_service = true
|
||||
[plugins.linux]
|
||||
shim = "%s"
|
||||
shim_debug = true
|
||||
[plugins.cri.containerd.runtimes.` + containerdRuntime + `]
|
||||
runtime_type = "io.containerd.runtime.v1.linux"
|
||||
runtime_engine = "%s"
|
||||
runtime_root = "%s/root/runsc"
|
||||
`
|
||||
|
||||
const v2Template = `
|
||||
// Template is the containerd configuration file that configures containerd with
|
||||
// the gVisor shim, Note that the v2 shim binary name must be
|
||||
// containerd-shim-<runtime>-v1.
|
||||
const template = `
|
||||
disabled_plugins = ["restart"]
|
||||
[plugins.cri]
|
||||
disable_tcp_service = true
|
||||
@@ -311,22 +285,11 @@ disabled_plugins = ["restart"]
|
||||
TypeUrl = "io.containerd.` + containerdRuntime + `.v1.options"
|
||||
`
|
||||
|
||||
const (
|
||||
// v1 is the containerd API v1.
|
||||
v1 string = "v1"
|
||||
|
||||
// v2 is the containerd API v2.
|
||||
v2 string = "v2"
|
||||
)
|
||||
|
||||
// allVersions is the set of known versions.
|
||||
var allVersions = []string{v1, v2}
|
||||
|
||||
// setup sets up before a test. Specifically it:
|
||||
// * Creates directories and a socket for containerd to utilize.
|
||||
// * Runs containerd and waits for it to reach a "ready" state for testing.
|
||||
// * Returns a cleanup function that should be called at the end of the test.
|
||||
func setup(t *testing.T, version string) (*criutil.Crictl, func(), error) {
|
||||
func setup(t *testing.T) (*criutil.Crictl, func(), error) {
|
||||
// Create temporary containerd root and state directories, and a socket
|
||||
// via which crictl and containerd communicate.
|
||||
containerdRoot, err := ioutil.TempDir(testutil.TmpDir(), "containerd-root")
|
||||
@@ -373,6 +336,11 @@ func setup(t *testing.T, version string) (*criutil.Crictl, func(), error) {
|
||||
}
|
||||
t.Logf("Using containerd version: %d.%d", major, minor)
|
||||
|
||||
// Check if containerd supports shim v2.
|
||||
if major < 1 || (major == 1 && minor <= 1) {
|
||||
t.Skipf("skipping incompatible containerd (want at least 1.2, got %d.%d)", major, minor)
|
||||
}
|
||||
|
||||
// We rewrite a configuration. This is based on the current docker
|
||||
// configuration for the runtime under test.
|
||||
runtime, err := dockerutil.RuntimePath()
|
||||
@@ -393,37 +361,9 @@ func setup(t *testing.T, version string) (*criutil.Crictl, func(), error) {
|
||||
modifiedPath = runtimeDir + ":" + modifiedPath
|
||||
t.Logf("Using PATH: %v", modifiedPath)
|
||||
|
||||
var (
|
||||
config string
|
||||
runpArgs []string
|
||||
)
|
||||
switch version {
|
||||
case v1:
|
||||
// This is only supported less than 1.3.
|
||||
if major > 1 || (major == 1 && minor >= 3) {
|
||||
t.Skipf("skipping unsupported containerd (want less than 1.3, got %d.%d)", major, minor)
|
||||
}
|
||||
|
||||
// We provide the shim, followed by the runtime, and then a
|
||||
// temporary root directory.
|
||||
config = fmt.Sprintf(v1Template, criutil.ResolvePath("gvisor-containerd-shim"), runtime, containerdRoot)
|
||||
case v2:
|
||||
// This is only supported past 1.2.
|
||||
if major < 1 || (major == 1 && minor <= 1) {
|
||||
t.Skipf("skipping incompatible containerd (want at least 1.2, got %d.%d)", major, minor)
|
||||
}
|
||||
|
||||
// The runtime is provided via parameter. Note that the v2 shim
|
||||
// binary name is always containerd-shim-* so we don't actually
|
||||
// care about the docker runtime name.
|
||||
config = v2Template
|
||||
default:
|
||||
t.Fatalf("unknown version: %s", version)
|
||||
}
|
||||
t.Logf("Using config: %s", config)
|
||||
|
||||
// Generate the configuration for the test.
|
||||
configFile, configCleanup, err := testutil.WriteTmpFile("containerd-config", config)
|
||||
t.Logf("Using config: %s", template)
|
||||
configFile, configCleanup, err := testutil.WriteTmpFile("containerd-config", template)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write containerd config")
|
||||
}
|
||||
@@ -488,7 +428,7 @@ func setup(t *testing.T, version string) (*criutil.Crictl, func(), error) {
|
||||
go io.Copy(ioutil.Discard, startupR)
|
||||
|
||||
// Create the crictl interface.
|
||||
cc := criutil.NewCrictl(t, sockAddr, runpArgs)
|
||||
cc := criutil.NewCrictl(t, sockAddr)
|
||||
cu.Add(cc.CleanUp)
|
||||
|
||||
// Kill must be the last cleanup (as it will be executed first).
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ EOF
|
||||
# There are a few solitary files that can get left behind due to the way bazel
|
||||
# constructs the gopath target. Note that we don't find all Go files here
|
||||
# because they may correspond to unused templates, etc.
|
||||
declare -ar binaries=( "runsc" "shim/v1" "shim/v2" "webhook" )
|
||||
declare -ar binaries=( "runsc" "shim" "webhook" )
|
||||
for target in "${binaries[@]}"; do
|
||||
mkdir -p "${target}"
|
||||
cp "${repo_orig}/${target}"/*.go "${target}/"
|
||||
|
||||
@@ -27,7 +27,6 @@ sh_binary(
|
||||
name = "shim",
|
||||
srcs = ["shim.sh"],
|
||||
data = [
|
||||
"//shim/v1:gvisor-containerd-shim",
|
||||
"//shim/v2:containerd-shim-runsc-v1",
|
||||
"//shim:containerd-shim-runsc-v1",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -75,14 +75,11 @@ install_helper github.com/containerd/containerd "v${CONTAINERD_VERSION}" "${GOPA
|
||||
install_helper github.com/kubernetes-sigs/cri-tools "v${CRITOOLS_VERSION}" "${GOPATH}"
|
||||
|
||||
# Configure containerd-shim.
|
||||
#
|
||||
# Note that for versions <= 1.1 the legacy shim must be installed in /usr/bin,
|
||||
# which should align with the installer script in head.sh (or master.sh).
|
||||
if [[ "${CONTAINERD_MAJOR}" -le 1 ]] && [[ "${CONTAINERD_MINOR}" -lt 2 ]]; then
|
||||
declare -r shim_config_path=/etc/containerd/gvisor-containerd-shim.toml
|
||||
mkdir -p $(dirname ${shim_config_path})
|
||||
cat > ${shim_config_path} <<-EOF
|
||||
runc_shim = "/usr/bin/containerd-shim"
|
||||
declare -r shim_config_path=/etc/containerd/runsc/config.toml
|
||||
mkdir -p $(dirname ${shim_config_path})
|
||||
cat > ${shim_config_path} <<-EOF
|
||||
log_path = "/tmp/shim-logs/"
|
||||
log_level = "debug"
|
||||
|
||||
[runsc_config]
|
||||
debug = "true"
|
||||
@@ -90,7 +87,6 @@ if [[ "${CONTAINERD_MAJOR}" -le 1 ]] && [[ "${CONTAINERD_MINOR}" -lt 2 ]]; then
|
||||
strace = "true"
|
||||
file-access = "shared"
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Configure CNI.
|
||||
(cd "${GOPATH}" && src/github.com/containerd/containerd/script/setup/install-cni)
|
||||
|
||||
@@ -30,4 +30,3 @@ if [[ -d "$0.runfiles" ]]; then
|
||||
runfiles="$0.runfiles"
|
||||
fi
|
||||
find -L "${runfiles}" -executable -type f -name containerd-shim-runsc-v1 -exec cp -L {} "${containerd_install_dir}" \;
|
||||
find -L "${runfiles}" -executable -type f -name gvisor-containerd-shim -exec cp -L {} "${containerd_install_dir}" \;
|
||||
|
||||
+18
-15
@@ -16,21 +16,24 @@ categories:
|
||||
<ul class="sidebar-nav">
|
||||
{% assign subcats = site.pages | where: 'layout', 'docs' | where: 'category', category | group_by: 'subcategory' | sort: 'name', 'first' %}
|
||||
{% for subcategory in subcats %}
|
||||
{% assign sorted_pages = subcategory.items | sort: 'weight', 'last' %}
|
||||
{% if subcategory.name != "" %}
|
||||
{% assign ac = "aria-controls" %}
|
||||
{% assign cid = category | remove: " " | downcase %}
|
||||
{% assign sid = subcategory.name | remove: " " | downcase %}
|
||||
<li>
|
||||
<a class="sidebar-nav-heading" data-toggle="collapse" href="#{{ cid }}-{{ sid }}" aria-expanded="false" {{ ac }}="{{ cid }}-{{ sid }}">{{ subcategory.name }}<span class="caret"></span></a>
|
||||
<ul class="collapse sidebar-nav sidebar-submenu" id="{{ cid }}-{{ sid }}">
|
||||
{% endif %}
|
||||
{% for p in sorted_pages %}
|
||||
<li><a href="{{ p.url }}">{{ p.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% if subcategory.name != "" %}
|
||||
</li>
|
||||
</ul>
|
||||
{% assign sorted_pages = subcategory.items | where: 'include_in_menu', true | sort: 'weight', 'last' %}
|
||||
{% comment %}If all pages in the subcategory are excluded don't show it.{% endcomment %}
|
||||
{% if sorted_pages.size > 0 %}
|
||||
{% if subcategory.name != "" %}
|
||||
{% assign ac = "aria-controls" %}
|
||||
{% assign cid = category | remove: " " | downcase %}
|
||||
{% assign sid = subcategory.name | remove: " " | downcase %}
|
||||
<li>
|
||||
<a class="sidebar-nav-heading" data-toggle="collapse" href="#{{ cid }}-{{ sid }}" aria-expanded="false" {{ ac }}="{{ cid }}-{{ sid }}">{{ subcategory.name }}<span class="caret"></span></a>
|
||||
<ul class="collapse sidebar-nav sidebar-submenu" id="{{ cid }}-{{ sid }}">
|
||||
{% endif %}
|
||||
{% for p in sorted_pages %}
|
||||
<li><a href="{{ p.url }}">{{ p.title }}</a></li>
|
||||
{% endfor %}
|
||||
{% if subcategory.name != "" %}
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
+9
-1
@@ -7,6 +7,7 @@ load("//tools:defs.bzl", "short_path")
|
||||
# dynamically. This is done the via BUILD system so that the plain
|
||||
# documentation files can be viewable without non-compliant markdown headers.
|
||||
DocInfo = provider(
|
||||
"Encapsulates information for a documentation page.",
|
||||
fields = [
|
||||
"layout",
|
||||
"description",
|
||||
@@ -16,6 +17,7 @@ DocInfo = provider(
|
||||
"weight",
|
||||
"editpath",
|
||||
"authors",
|
||||
"include_in_menu",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -33,6 +35,7 @@ def _doc_impl(ctx):
|
||||
weight = ctx.attr.weight,
|
||||
editpath = short_path(ctx.files.src[0].short_path),
|
||||
authors = ctx.attr.authors,
|
||||
include_in_menu = ctx.attr.include_in_menu,
|
||||
),
|
||||
]
|
||||
|
||||
@@ -74,6 +77,10 @@ doc = rule(
|
||||
default = "50",
|
||||
),
|
||||
"authors": attr.string_list(),
|
||||
"include_in_menu": attr.bool(
|
||||
doc = "Include document in the navigation menu.",
|
||||
default = True,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -111,7 +118,8 @@ subcategory: {subcategory}
|
||||
weight: {weight}
|
||||
editpath: {editpath}
|
||||
authors: {authors}
|
||||
layout: {layout}"""
|
||||
layout: {layout}
|
||||
include_in_menu: {include_in_menu}"""
|
||||
|
||||
for f in dep.files.to_list():
|
||||
# Is this a markdown file? If not, then we ensure that it ends up
|
||||
|
||||
Reference in New Issue
Block a user