diff --git a/cmd/ctr/commands/commands.go b/cmd/ctr/commands/commands.go index d0e52a652..b3ba7c9ca 100644 --- a/cmd/ctr/commands/commands.go +++ b/cmd/ctr/commands/commands.go @@ -134,7 +134,7 @@ var ( }, cli.StringFlag{ Name: "runtime", - Usage: "runtime name", + Usage: "runtime name or absolute path to runtime binary", Value: defaults.DefaultRuntime, }, cli.StringFlag{ diff --git a/runtime/v2/manager.go b/runtime/v2/manager.go index 1927cbb3f..b8db39983 100644 --- a/runtime/v2/manager.go +++ b/runtime/v2/manager.go @@ -22,6 +22,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "sync" "github.com/containerd/containerd/containers" @@ -244,6 +245,11 @@ func (m *ShimManager) resolveRuntimePath(runtime string) (string, error) { return runtime, nil } + // Check if relative path to runtime binary provided + if strings.Contains(runtime, "/") { + 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) + } + // Preserve existing logic and resolve runtime path from runtime name. name := shimbinary.BinaryName(runtime) diff --git a/runtime/v2/manager_test.go b/runtime/v2/manager_test.go new file mode 100644 index 000000000..b6a2b8940 --- /dev/null +++ b/runtime/v2/manager_test.go @@ -0,0 +1,99 @@ +//go:build !windows && !darwin +// +build !windows,!darwin + +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package v2 + +import ( + "os" + "testing" +) + +// setupAbsoluteShimPath creates a temporary directory in $PATH with an empty +// shim executable file in it to test the exec.LookPath branch of resolveRuntimePath +func setupAbsoluteShimPath(t *testing.T) (string, error) { + tempShimDir := t.TempDir() + + _, err := os.Create(tempShimDir + "/containerd-shim-runc-v2") + if err != nil { + return "", err + } + + t.Setenv("PATH", tempShimDir+":"+os.Getenv("PATH")) + absoluteShimPath := tempShimDir + "/containerd-shim-runc-v2" + + err = os.Chmod(absoluteShimPath, 0777) + if err != nil { + return "", err + } + + return absoluteShimPath, nil +} + +func TestResolveRuntimePath(t *testing.T) { + sm := &ShimManager{} + absoluteShimPath, err := setupAbsoluteShimPath(t) + if err != nil { + t.Errorf("Failed to create temporary shim path: %q", err) + } + + tests := []struct { + runtime string + want string + }{ + { // Absolute path + runtime: absoluteShimPath, + want: absoluteShimPath, + }, + { // Binary name + runtime: "io.containerd.runc.v2", + want: absoluteShimPath, + }, + { // Invalid absolute path + runtime: "/fake/abs/path", + want: "", + }, + { // No name + runtime: "", + want: "", + }, + { // Relative Path + runtime: "./containerd-shim-runc-v2", + want: "", + }, + { + runtime: "fake/containerd-shim-runc-v2", + want: "", + }, + { + runtime: "./fake/containerd-shim-runc-v2", + want: "", + }, + { // Relative Path or Bad Binary Name + runtime: ".io.containerd.runc.v2", + want: "", + }, + } + + for _, c := range tests { + have, _ := sm.resolveRuntimePath(c.runtime) + if have != c.want { + t.Errorf("Expected %q, got %q", c.want, have) + } + } +} diff --git a/runtime/v2/shim/util.go b/runtime/v2/shim/util.go index 28ac9d1e7..a90d004bb 100644 --- a/runtime/v2/shim/util.go +++ b/runtime/v2/shim/util.go @@ -85,7 +85,7 @@ func Command(ctx context.Context, config *CommandConfig) (*exec.Cmd, error) { func BinaryName(runtime string) string { // runtime name should format like $prefix.name.version parts := strings.Split(runtime, ".") - if len(parts) < 2 { + if len(parts) < 2 || parts[0] == "" { return "" }