X Tutup
Skip to content

Commit d055487

Browse files
authored
Merge pull request containerd#6206 from mxpv/path
Allow absolute path to shim binaries
2 parents 77e76d7 + e17fe37 commit d055487

File tree

15 files changed

+422
-230
lines changed

15 files changed

+422
-230
lines changed

api/next.pb.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3821,6 +3821,13 @@ file {
38213821
type_name: ".google.protobuf.Any"
38223822
json_name: "options"
38233823
}
3824+
field {
3825+
name: "runtime_path"
3826+
number: 10
3827+
label: LABEL_OPTIONAL
3828+
type: TYPE_STRING
3829+
json_name: "runtimePath"
3830+
}
38243831
}
38253832
message_type {
38263833
name: "CreateTaskResponse"

api/services/tasks/v1/tasks.pb.go

Lines changed: 133 additions & 86 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/services/tasks/v1/tasks.proto

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ message CreateTaskRequest {
8888
containerd.types.Descriptor checkpoint = 8;
8989

9090
google.protobuf.Any options = 9;
91+
92+
string runtime_path = 10;
9193
}
9294

9395
message CreateTaskResponse {

pkg/cri/config/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ import (
3131
type Runtime struct {
3232
// Type is the runtime type to use in containerd e.g. io.containerd.runtime.v1.linux
3333
Type string `toml:"runtime_type" json:"runtimeType"`
34+
// Path is an optional field that can be used to overwrite path to a shim runtime binary.
35+
// When specified, containerd will ignore runtime name field when resolving shim location.
36+
// Path must be abs.
37+
Path string `toml:"runtime_path" json:"runtimePath"`
3438
// Engine is the name of the runtime engine used by containerd.
3539
// This only works for runtime type "io.containerd.runtime.v1.linux".
3640
// DEPRECATED: use Options instead. Remove when shim v1 is deprecated.

pkg/cri/server/container_start.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,15 @@ func (c *criService) StartContainer(ctx context.Context, r *runtime.StartContain
110110
return nil, errors.Wrap(err, "failed to get container info")
111111
}
112112

113+
ociRuntime, err := c.getSandboxRuntime(sandbox.Config, sandbox.Metadata.RuntimeHandler)
114+
if err != nil {
115+
return nil, errors.Wrap(err, "failed to get sandbox runtime")
116+
}
117+
113118
taskOpts := c.taskOpts(ctrInfo.Runtime.Name)
119+
if ociRuntime.Path != "" {
120+
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
121+
}
114122
task, err := container.NewTask(ctx, ioCreation, taskOpts...)
115123
if err != nil {
116124
return nil, errors.Wrap(err, "failed to create containerd task")

pkg/cri/server/sandbox_run.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ func (c *criService) RunPodSandbox(ctx context.Context, r *runtime.RunPodSandbox
281281
id, name)
282282

283283
taskOpts := c.taskOpts(ociRuntime.Type)
284+
if ociRuntime.Path != "" {
285+
taskOpts = append(taskOpts, containerd.WithRuntimePath(ociRuntime.Path))
286+
}
284287
// We don't need stdio for sandbox container.
285288
task, err := container.NewTask(ctx, containerdio.NullIO, taskOpts...)
286289
if err != nil {

runtime/runtime.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ type CreateOpts struct {
4646
RuntimeOptions *types.Any
4747
// TaskOptions received for the task
4848
TaskOptions *types.Any
49-
// Runtime to use
49+
// Runtime name to use (e.g. `io.containerd.NAME.VERSION`).
50+
// As an alternative full abs path to binary may be specified instead.
5051
Runtime string
5152
}
5253

runtime/v2/binary.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"context"
2222
"io"
2323
"os"
24+
"path/filepath"
2425
gruntime "runtime"
2526
"strings"
2627

@@ -127,6 +128,10 @@ func (b *binary) Start(ctx context.Context, opts *types.Any, onClose func()) (_
127128
cancelShimLog()
128129
f.Close()
129130
}
131+
// Save runtime binary path for restore.
132+
if err := os.WriteFile(filepath.Join(b.bundle.Path, "runtime"), []byte(b.runtime), 0600); err != nil {
133+
return nil, err
134+
}
130135
client := ttrpc.NewClient(conn, ttrpc.WithOnClose(onCloseWithShimLog))
131136
return &shim{
132137
bundle: b.bundle,

runtime/v2/manager.go

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import (
2020
"context"
2121
"fmt"
2222
"os"
23+
"os/exec"
24+
"path/filepath"
25+
"sync"
2326

2427
"github.com/containerd/containerd/containers"
2528
"github.com/containerd/containerd/errdefs"
@@ -31,6 +34,7 @@ import (
3134
"github.com/containerd/containerd/platforms"
3235
"github.com/containerd/containerd/plugin"
3336
"github.com/containerd/containerd/runtime"
37+
shimbinary "github.com/containerd/containerd/runtime/v2/shim"
3438
"github.com/containerd/containerd/runtime/v2/task"
3539
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3640
"github.com/pkg/errors"
@@ -199,6 +203,8 @@ type ShimManager struct {
199203
shims *runtime.TaskList
200204
events *exchange.Exchange
201205
containers containers.Store
206+
// runtimePaths is a cache of `runtime names` -> `resolved fs path`
207+
runtimePaths sync.Map
202208
}
203209

204210
// ID of the shim manager
@@ -253,8 +259,13 @@ func (m *ShimManager) startShim(ctx context.Context, bundle *Bundle, id string,
253259
topts = opts.RuntimeOptions
254260
}
255261

262+
runtimePath, err := m.resolveRuntimePath(opts.Runtime)
263+
if err != nil {
264+
return nil, fmt.Errorf("failed to resolve runtime path: %w", err)
265+
}
266+
256267
b := shimBinary(bundle, shimBinaryConfig{
257-
runtime: opts.Runtime,
268+
runtime: runtimePath,
258269
address: m.containerdAddress,
259270
ttrpcAddress: m.containerdTTRPCAddress,
260271
schedCore: m.schedCore,
@@ -276,6 +287,79 @@ func (m *ShimManager) startShim(ctx context.Context, bundle *Bundle, id string,
276287
return shim, nil
277288
}
278289

290+
func (m *ShimManager) resolveRuntimePath(runtime string) (string, error) {
291+
if runtime == "" {
292+
return "", fmt.Errorf("no runtime name")
293+
}
294+
295+
// Custom path to runtime binary
296+
if filepath.IsAbs(runtime) {
297+
// Make sure it exists before returning ok
298+
if _, err := os.Stat(runtime); err != nil {
299+
return "", fmt.Errorf("invalid custom binary path: %w", err)
300+
}
301+
302+
return runtime, nil
303+
}
304+
305+
// Preserve existing logic and resolve runtime path from runtime name.
306+
307+
name := shimbinary.BinaryName(runtime)
308+
if name == "" {
309+
return "", fmt.Errorf("invalid runtime name %s, correct runtime name should be either format like `io.containerd.runc.v1` or a full path to the binary", runtime)
310+
}
311+
312+
if path, ok := m.runtimePaths.Load(name); ok {
313+
return path.(string), nil
314+
}
315+
316+
var (
317+
cmdPath string
318+
lerr error
319+
)
320+
321+
binaryPath := shimbinary.BinaryPath(runtime)
322+
if _, serr := os.Stat(binaryPath); serr == nil {
323+
cmdPath = binaryPath
324+
}
325+
326+
if cmdPath == "" {
327+
if cmdPath, lerr = exec.LookPath(name); lerr != nil {
328+
if eerr, ok := lerr.(*exec.Error); ok {
329+
if eerr.Err == exec.ErrNotFound {
330+
self, err := os.Executable()
331+
if err != nil {
332+
return "", err
333+
}
334+
335+
// Match the calling binaries (containerd) path and see
336+
// if they are side by side. If so, execute the shim
337+
// found there.
338+
testPath := filepath.Join(filepath.Dir(self), name)
339+
if _, serr := os.Stat(testPath); serr == nil {
340+
cmdPath = testPath
341+
}
342+
if cmdPath == "" {
343+
return "", errors.Wrapf(os.ErrNotExist, "runtime %q binary not installed %q", runtime, name)
344+
}
345+
}
346+
}
347+
}
348+
}
349+
350+
cmdPath, err := filepath.Abs(cmdPath)
351+
if err != nil {
352+
return "", err
353+
}
354+
355+
if path, ok := m.runtimePaths.LoadOrStore(name, cmdPath); ok {
356+
// We didn't store cmdPath we loaded an already cached value. Use it.
357+
cmdPath = path.(string)
358+
}
359+
360+
return cmdPath, nil
361+
}
362+
279363
// cleanupShim attempts to properly delete and cleanup shim after error
280364
func (m *ShimManager) cleanupShim(shim *shim) {
281365
dctx, cancel := timeout.WithContext(context.Background(), cleanupTimeout)

runtime/v2/shim/util.go

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"os"
2525
"path/filepath"
2626
"strings"
27-
"sync"
2827
"time"
2928

3029
"github.com/containerd/containerd/namespaces"
@@ -34,8 +33,6 @@ import (
3433
exec "golang.org/x/sys/execabs"
3534
)
3635

37-
var runtimePaths sync.Map
38-
3936
type CommandConfig struct {
4037
Runtime string
4138
Address string
@@ -62,51 +59,7 @@ func Command(ctx context.Context, config *CommandConfig) (*exec.Cmd, error) {
6259
"-publish-binary", self,
6360
}
6461
args = append(args, config.Args...)
65-
name := BinaryName(config.Runtime)
66-
if name == "" {
67-
return nil, fmt.Errorf("invalid runtime name %s, correct runtime name should format like io.containerd.runc.v1", config.Runtime)
68-
}
69-
70-
var cmdPath string
71-
cmdPathI, cmdPathFound := runtimePaths.Load(name)
72-
if cmdPathFound {
73-
cmdPath = cmdPathI.(string)
74-
} else {
75-
var lerr error
76-
binaryPath := BinaryPath(config.Runtime)
77-
if _, serr := os.Stat(binaryPath); serr == nil {
78-
cmdPath = binaryPath
79-
}
80-
81-
if cmdPath == "" {
82-
if cmdPath, lerr = exec.LookPath(name); lerr != nil {
83-
if eerr, ok := lerr.(*exec.Error); ok {
84-
if eerr.Err == exec.ErrNotFound {
85-
// Match the calling binaries (containerd) path and see
86-
// if they are side by side. If so, execute the shim
87-
// found there.
88-
testPath := filepath.Join(filepath.Dir(self), name)
89-
if _, serr := os.Stat(testPath); serr == nil {
90-
cmdPath = testPath
91-
}
92-
if cmdPath == "" {
93-
return nil, errors.Wrapf(os.ErrNotExist, "runtime %q binary not installed %q", config.Runtime, name)
94-
}
95-
}
96-
}
97-
}
98-
}
99-
cmdPath, err = filepath.Abs(cmdPath)
100-
if err != nil {
101-
return nil, err
102-
}
103-
if cmdPathI, cmdPathFound = runtimePaths.LoadOrStore(name, cmdPath); cmdPathFound {
104-
// We didn't store cmdPath we loaded an already cached value. Use it.
105-
cmdPath = cmdPathI.(string)
106-
}
107-
}
108-
109-
cmd := exec.CommandContext(ctx, cmdPath, args...)
62+
cmd := exec.CommandContext(ctx, config.Runtime, args...)
11063
cmd.Dir = config.Path
11164
cmd.Env = append(
11265
os.Environ(),

0 commit comments

Comments
 (0)
X Tutup