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:
Fabricio Voznika
2021-01-12 17:54:10 -08:00
committed by gVisor bot
parent 72ef787749
commit 29ebab9ef0
63 changed files with 220 additions and 1467 deletions
+1 -2
View File
@@ -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.
+3 -8
View File
@@ -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)
+2 -3
View File
@@ -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
+1 -2
View File
@@ -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",
+1 -1
View File
@@ -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",
)
+1 -1
View File
@@ -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.
+2 -4
View File
@@ -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
)
```
-1
View File
@@ -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
-3
View File
@@ -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=
+1 -1
View File
@@ -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.
+4 -4
View File
@@ -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",
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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 (
-41
View File
@@ -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",
],
)
-41
View File
@@ -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
-106
View File
@@ -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
}
-572
View File
@@ -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
}
-17
View File
@@ -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
+1 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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/
+2 -2
View File
@@ -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",
],
)
+3 -3
View File
@@ -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)
}
+1 -1
View File
@@ -16,7 +16,7 @@
package main
import (
"gvisor.dev/gvisor/shim/v2/cli"
"gvisor.dev/gvisor/shim/cli"
)
func main() {
-13
View File
@@ -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"],
)
-30
View File
@@ -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",
],
)
-24
View File
@@ -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
-266
View File
@@ -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
}
-40
View File
@@ -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
}
-24
View File
@@ -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()
}
-13
View File
@@ -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
View File
@@ -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
View File
@@ -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}/"
+1 -2
View File
@@ -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",
],
)
+5 -9
View File
@@ -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)
-1
View File
@@ -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
View File
@@ -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
View File
@@ -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