mirror of
https://github.com/moby/moby.git
synced 2026-01-11 10:41:43 +00:00
Merge pull request #51624 from AkihiroSuda/fix-51601
cdi: read XDG_CONFIG_HOME/cdi and XDG_RUNTIME_DIR/cdi for rootless
This commit is contained in:
@@ -190,24 +190,6 @@ if [ -z "$_DOCKERD_ROOTLESS_CHILD" ]; then
|
||||
else
|
||||
[ "$_DOCKERD_ROOTLESS_CHILD" = 1 ]
|
||||
|
||||
# The Container Device Interface (CDI) specs can be found by default
|
||||
# under {/etc,/var/run}/cdi. More information at:
|
||||
# https://github.com/cncf-tags/container-device-interface
|
||||
#
|
||||
# In order to use the Container Device Interface (CDI) integration,
|
||||
# the CDI paths need to exist before the Docker daemon is started in
|
||||
# order for it to read the CDI specification files. Otherwise, a
|
||||
# Docker daemon restart will be required for the daemon to discover
|
||||
# them.
|
||||
#
|
||||
# If another set of CDI paths (other than the default /etc/cdi and
|
||||
# /var/run/cdi) are configured through the Docker configuration file
|
||||
# (using "cdi-spec-dirs"), they need to be bind mounted in rootless
|
||||
# mode; otherwise the Docker daemon won't have access to the CDI
|
||||
# specification files.
|
||||
mount_directory /etc/cdi
|
||||
mount_directory /var/run/cdi
|
||||
|
||||
# remove the symlinks for the existing files in the parent namespace if any,
|
||||
# so that we can create our own files in our mount namespace.
|
||||
rm -f /run/docker /run/containerd /run/xtables.lock
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containerd/log"
|
||||
"github.com/moby/moby/api/types/system"
|
||||
@@ -22,6 +23,14 @@ type cdiHandler struct {
|
||||
// The driver injects CDI devices into an incoming OCI spec and is called for DeviceRequests associated with CDI devices.
|
||||
// If the list of CDI spec directories is empty, the driver is not registered.
|
||||
func RegisterCDIDriver(cdiSpecDirs ...string) *cdi.Cache {
|
||||
for i, dir := range cdiSpecDirs {
|
||||
if _, err := os.Stat(dir); !errors.Is(err, os.ErrNotExist) {
|
||||
cdiSpecDirs[i], err = filepath.EvalSymlinks(dir)
|
||||
if err != nil {
|
||||
log.L.WithField("dir", dir).WithError(err).Warn("Failed to evaluate symlinks for CDI spec directory")
|
||||
}
|
||||
}
|
||||
}
|
||||
driver, cache := newCDIDeviceDriver(cdiSpecDirs...)
|
||||
registerDeviceDriver("cdi", driver)
|
||||
return cache
|
||||
|
||||
@@ -57,6 +57,7 @@ import (
|
||||
"github.com/moby/moby/v2/pkg/homedir"
|
||||
"github.com/moby/moby/v2/pkg/pidfile"
|
||||
"github.com/moby/moby/v2/pkg/plugingetter"
|
||||
"github.com/moby/sys/userns"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/pflag"
|
||||
@@ -666,7 +667,35 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
|
||||
if conf.CDISpecDirs == nil {
|
||||
// If the CDISpecDirs is not set at this stage, we set it to the default.
|
||||
conf.CDISpecDirs = append([]string(nil), cdi.DefaultSpecDirs...)
|
||||
} else if len(conf.CDISpecDirs) == 1 && conf.CDISpecDirs[0] == "" {
|
||||
if rootless.RunningWithRootlessKit() {
|
||||
// In rootless mode, we add the user-specific CDI spec directory.
|
||||
xch, err := homedir.GetConfigHome()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
xrd, err := homedir.GetRuntimeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf.CDISpecDirs = append(conf.CDISpecDirs, filepath.Join(xch, "cdi"), filepath.Join(xrd, "cdi"))
|
||||
}
|
||||
}
|
||||
// Filter out CDI spec directories that are not readable, and log appropriately
|
||||
var cdiSpecDirs []string
|
||||
for _, dir := range conf.CDISpecDirs {
|
||||
// Non-existing directories are not filtered out here, as CDI spec directories are allowed to not exist.
|
||||
if _, err := os.ReadDir(dir); err == nil || errors.Is(err, os.ErrNotExist) {
|
||||
cdiSpecDirs = append(cdiSpecDirs, dir)
|
||||
} else {
|
||||
logLevel := log.ErrorLevel
|
||||
if userns.RunningInUserNS() && errors.Is(err, os.ErrPermission) {
|
||||
logLevel = log.DebugLevel
|
||||
}
|
||||
log.L.WithField("dir", dir).WithError(err).Log(logLevel, "CDI spec directory cannot be accessed, skipping")
|
||||
}
|
||||
}
|
||||
conf.CDISpecDirs = cdiSpecDirs
|
||||
if len(conf.CDISpecDirs) == 1 && conf.CDISpecDirs[0] == "" {
|
||||
// If CDISpecDirs is set to an empty string, we clear it to ensure that CDI is disabled.
|
||||
conf.CDISpecDirs = nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package container
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -203,3 +204,64 @@ func TestCDIInfoDiscoveredDevices(t *testing.T) {
|
||||
assert.Check(t, is.Equal(len(info.DiscoveredDevices), 1), "Expected one discovered device")
|
||||
assert.Check(t, is.DeepEqual(info.DiscoveredDevices, []system.DeviceInfo{expectedDevice}))
|
||||
}
|
||||
|
||||
// TestEtcCDI verifies that the daemon picks up CDI specs from /etc/cdi, even in rootless mode.
|
||||
// Added for https://github.com/moby/moby/pull/51624
|
||||
func TestEtcCDI(t *testing.T) {
|
||||
skip.If(t, testEnv.IsRemoteDaemon, "cannot run daemon when remote daemon")
|
||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "CDI not supported on Windows")
|
||||
|
||||
ctx := testutil.StartSpan(baseContext, t)
|
||||
|
||||
// Create a sample CDI spec file
|
||||
specContent := `{
|
||||
"cdiVersion": "0.5.0",
|
||||
"kind": "test.mobyproject.org/device",
|
||||
"devices": [
|
||||
{
|
||||
"name": "mygpu0",
|
||||
"containerEdits": {
|
||||
"deviceNodes": [
|
||||
{"path": "/dev/null"}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
cdiDir := "/etc/cdi"
|
||||
_, err := os.Stat(cdiDir)
|
||||
cdiDirExisted := !errors.Is(err, os.ErrNotExist)
|
||||
err = os.MkdirAll(cdiDir, 0o755)
|
||||
assert.NilError(t, err, "Failed to create /etc/cdi directory")
|
||||
|
||||
t.Cleanup(func() {
|
||||
if !cdiDirExisted {
|
||||
rmErr := os.Remove(cdiDir)
|
||||
assert.NilError(t, rmErr, "Failed to remove /etc/cdi directory")
|
||||
}
|
||||
})
|
||||
|
||||
specFilePath := filepath.Join(cdiDir, "moby-integration-test-device.json")
|
||||
err = os.WriteFile(specFilePath, []byte(specContent), 0o644)
|
||||
assert.NilError(t, err, "Failed to write sample CDI spec file")
|
||||
t.Cleanup(func() {
|
||||
rmErr := os.RemoveAll(specFilePath)
|
||||
assert.NilError(t, rmErr, "Failed to remove sample CDI spec file")
|
||||
})
|
||||
|
||||
d := daemon.New(t)
|
||||
d.Start(t, "--feature", "cdi")
|
||||
defer d.Stop(t)
|
||||
|
||||
c := d.NewClientT(t)
|
||||
result, err := c.Info(ctx, client.InfoOptions{})
|
||||
assert.NilError(t, err)
|
||||
info := result.Info
|
||||
|
||||
expectedDevice := system.DeviceInfo{
|
||||
Source: "cdi",
|
||||
ID: "test.mobyproject.org/device=mygpu0",
|
||||
}
|
||||
assert.Check(t, is.Contains(info.DiscoveredDevices, expectedDevice))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user