From e7f84c06cf7cef488d0d068d7e6c9c1e9f220c93 Mon Sep 17 00:00:00 2001 From: R0CKSTAR Date: Mon, 14 Feb 2022 14:53:24 +0800 Subject: [PATCH] Support container runtime other than runc Signed-off-by: R0CKSTAR --- .../runtime_factory.go | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/cmd/nvidia-container-runtime/runtime_factory.go b/cmd/nvidia-container-runtime/runtime_factory.go index 0af37f6e..329cb91b 100644 --- a/cmd/nvidia-container-runtime/runtime_factory.go +++ b/cmd/nvidia-container-runtime/runtime_factory.go @@ -17,7 +17,12 @@ package main import ( + "encoding/json" "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" "github.com/NVIDIA/nvidia-container-toolkit/internal/oci" ) @@ -26,8 +31,19 @@ const ( ociSpecFileName = "config.json" dockerRuncExecutableName = "docker-runc" runcExecutableName = "runc" + nvidiaRuntimeName = "nvidia" + runcRuntimeName = "runc" + dockerDefaultConfig = "/etc/docker/daemon.json" ) +type dockerDaemon struct { + Runtimes map[string]dockerRuntime `json:"runtimes,omitempty"` +} + +type dockerRuntime struct { + Path *string `json:"path,omitempty"` +} + // newRuntime is a factory method that constructs a runtime based on the selected configuration. func newRuntime(argv []string) (oci.Runtime, error) { ociSpec, err := newOCISpec(argv) @@ -35,12 +51,18 @@ func newRuntime(argv []string) (oci.Runtime, error) { return nil, fmt.Errorf("error constructing OCI specification: %v", err) } - runc, err := newRuncRuntime() + runtime, err := newDefaultRuntime() if err != nil { - return nil, fmt.Errorf("error constructing runc runtime: %v", err) + logger.Errorf("Error constructing default runtime: %v", err) + + runc, err := newRuncRuntime() + if err != nil { + return nil, fmt.Errorf("error constructing runc runtime: %v", err) + } + runtime = runc } - r, err := newNvidiaContainerRuntimeWithLogger(logger.Logger, runc, ociSpec) + r, err := newNvidiaContainerRuntimeWithLogger(logger.Logger, runtime, ociSpec) if err != nil { return nil, fmt.Errorf("error constructing NVIDIA Container Runtime: %v", err) } @@ -72,3 +94,47 @@ func newRuncRuntime() (oci.Runtime, error) { runcExecutableName, ) } + +func dockerRuntimeExecutablePath(name string) (string, error) { + file, err := os.Open(dockerDefaultConfig) + if err != nil { + return "", err + } + + bytes, err := ioutil.ReadAll(file) + if err != nil { + return "", err + } + + daemon := dockerDaemon{} + if err := json.Unmarshal(bytes, &daemon); err != nil { + return "", err + } + + return *daemon.Runtimes[name].Path, nil +} + +// newDefaultRuntime locates the default runtime binary and wraps it in a SyscallExecRuntime +func newDefaultRuntime() (oci.Runtime, error) { + cmd := exec.Command("docker", "info", "--format", "{{.DefaultRuntime}}") + output, err := cmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("error getting docker default runtime: %v", err) + } + defaultRuntimeName := strings.TrimSpace(string(output)) + if defaultRuntimeName == nvidiaRuntimeName || defaultRuntimeName == runcRuntimeName { + return nil, fmt.Errorf("docker default runtime is %v, bail out: %v", defaultRuntimeName, err) + } + candidates := []string{} + defaultRuntimeExecutablePath, err := dockerRuntimeExecutablePath(defaultRuntimeName) + if err != nil { + logger.Errorf("Error getting docker default runtime (%v)'s executable path: %v", defaultRuntimeName, err) + candidates = append(candidates, defaultRuntimeName) + } else { + candidates = append(candidates, defaultRuntimeExecutablePath) + } + return oci.NewLowLevelRuntimeWithLogger( + logger.Logger, + candidates..., + ) +}