From 282868dabf9bbff271216de5fd39f01038ddf309 Mon Sep 17 00:00:00 2001 From: Rob Murray Date: Tue, 2 Dec 2025 17:26:12 +0000 Subject: [PATCH] NRI: instantiate and start/stop NRI adaptation Signed-off-by: Rob Murray --- daemon/daemon.go | 14 ++++ daemon/internal/nri/nri.go | 123 +++++++++++++++++++++++++++++++++- pkg/homedir/homedir_linux.go | 10 +++ pkg/homedir/homedir_others.go | 5 ++ 4 files changed, 151 insertions(+), 1 deletion(-) diff --git a/daemon/daemon.go b/daemon/daemon.go index c72da06129..a2849cbfbf 100644 --- a/daemon/daemon.go +++ b/daemon/daemon.go @@ -37,6 +37,7 @@ import ( networktypes "github.com/moby/moby/api/types/network" registrytypes "github.com/moby/moby/api/types/registry" "github.com/moby/moby/api/types/swarm" + "github.com/moby/moby/v2/daemon/internal/nri" "github.com/moby/sys/user" "github.com/moby/sys/userns" "github.com/pkg/errors" @@ -116,6 +117,7 @@ type Daemon struct { shutdown bool idMapping user.IdentityMapping PluginStore *plugin.Store // TODO: remove + nri *nri.NRI pluginManager *plugin.Manager linkIndex *linkIndex containerdClient *containerd.Client @@ -1096,6 +1098,14 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S return nil, err } + d.nri, err = nri.NewNRI(ctx, nri.Config{ + DaemonConfig: config.NRIOpts, + ContainerLister: d.containers, + }) + if err != nil { + return nil, err + } + driverName := getDriverOverride(ctx, cfgStore.GraphDriver, imgStoreChoice) var migrationConfig migration.Config @@ -1486,6 +1496,10 @@ func (daemon *Daemon) Shutdown(ctx context.Context) error { // Shutdown plugins after containers and layerstore. Don't change the order. daemon.pluginShutdown() + if daemon.nri != nil { + daemon.nri.Shutdown(ctx) + } + // trigger libnetwork Stop only if it's initialized if daemon.netController != nil { daemon.netController.Stop() diff --git a/daemon/internal/nri/nri.go b/daemon/internal/nri/nri.go index c4c779a2fb..b525d400a3 100644 --- a/daemon/internal/nri/nri.go +++ b/daemon/internal/nri/nri.go @@ -1,7 +1,128 @@ package nri import ( + "context" + "errors" + "fmt" + "path/filepath" + "sync" + + "github.com/containerd/log" "github.com/containerd/nri/pkg/adaptation" + "github.com/moby/moby/v2/daemon/container" + "github.com/moby/moby/v2/daemon/internal/rootless" + "github.com/moby/moby/v2/daemon/pkg/opts" + "github.com/moby/moby/v2/dockerversion" + "github.com/moby/moby/v2/pkg/homedir" ) -var _ = *adaptation.Adaptation +const ( + // defaultPluginSubdir is the default location for NRI plugins under libexec, + // which is in a different location for rootful/rootless Docker. + defaultPluginSubdir = "docker/nri-plugins" + // defaultPluginConfigSubdir is the default location for NRI plugin config under etc, + // which is in a different location for rootful/rootless Docker. + defaultPluginConfigSubdir = "docker/nri/conf.d" +) + +type NRI struct { + cfg Config + + // mu protects nri - read lock for container operations, write lock for sync and shutdown. + mu sync.RWMutex + nri *adaptation.Adaptation +} + +type ContainerLister interface { + List() []*container.Container +} + +type Config struct { + DaemonConfig opts.NRIOpts + ContainerLister ContainerLister +} + +func NewNRI(ctx context.Context, cfg Config) (*NRI, error) { + n := &NRI{cfg: cfg} + if !n.cfg.DaemonConfig.Enable { + log.G(ctx).Info("NRI is disabled") + return n, nil + } + + if err := setDefaultPaths(&n.cfg.DaemonConfig); err != nil { + return nil, err + } + log.G(ctx).WithFields(log.Fields{ + "pluginPath": n.cfg.DaemonConfig.PluginPath, + "pluginConfigPath": n.cfg.DaemonConfig.PluginConfigPath, + "socketPath": n.cfg.DaemonConfig.SocketPath, + }).Info("Starting NRI") + + var err error + n.nri, err = adaptation.New("docker", dockerversion.Version, n.syncFn, n.updateFn, nriOptions(n.cfg.DaemonConfig)...) + if err != nil { + return nil, err + } + if err := n.nri.Start(); err != nil { + return nil, err + } + return n, nil +} + +func (n *NRI) Shutdown(ctx context.Context) { + n.mu.Lock() + defer n.mu.Unlock() + if n.nri == nil { + return + } + log.G(ctx).Info("Shutting down NRI") + n.nri.Stop() + n.nri = nil +} + +func (n *NRI) syncFn(ctx context.Context, syncCB adaptation.SyncCB) error { + return nil +} + +func (n *NRI) updateFn(context.Context, []*adaptation.ContainerUpdate) ([]*adaptation.ContainerUpdate, error) { + return nil, errors.New("not implemented") +} + +func setDefaultPaths(cfg *opts.NRIOpts) error { + if cfg.PluginPath != "" && cfg.PluginConfigPath != "" { + return nil + } + libexecDir := "/usr/libexec" + etcDir := "/etc" + if rootless.RunningWithRootlessKit() { + var err error + libexecDir, err = homedir.GetLibexecHome() + if err != nil { + return fmt.Errorf("configuring NRI: %w", err) + } + etcDir, err = homedir.GetConfigHome() + if err != nil { + return fmt.Errorf("configuring NRI: %w", err) + } + } + if cfg.PluginPath == "" { + cfg.PluginPath = filepath.Join(libexecDir, defaultPluginSubdir) + } + if cfg.PluginConfigPath == "" { + cfg.PluginConfigPath = filepath.Join(etcDir, defaultPluginConfigSubdir) + } + return nil +} + +func nriOptions(cfg opts.NRIOpts) []adaptation.Option { + res := []adaptation.Option{ + adaptation.WithPluginPath(cfg.PluginPath), + adaptation.WithPluginConfigPath(cfg.PluginConfigPath), + } + if cfg.SocketPath == "" { + res = append(res, adaptation.WithDisabledExternalConnections()) + } else { + res = append(res, adaptation.WithSocketPath(cfg.SocketPath)) + } + return res +} diff --git a/pkg/homedir/homedir_linux.go b/pkg/homedir/homedir_linux.go index 469395f16e..cb11fcb5e1 100644 --- a/pkg/homedir/homedir_linux.go +++ b/pkg/homedir/homedir_linux.go @@ -103,3 +103,13 @@ func GetLibHome() (string, error) { } return filepath.Join(home, ".local/lib"), nil } + +// GetLibexecHome returns $HOME/.local/libexec +// If HOME is not set, getpwent(3) is consulted to determine the users home directory. +func GetLibexecHome() (string, error) { + home := Get() + if home == "" { + return "", errors.New("could not get HOME") + } + return filepath.Join(home, ".local/libexec"), nil +} diff --git a/pkg/homedir/homedir_others.go b/pkg/homedir/homedir_others.go index 1e41e6aab5..ad4df82f7b 100644 --- a/pkg/homedir/homedir_others.go +++ b/pkg/homedir/homedir_others.go @@ -30,3 +30,8 @@ func GetConfigHome() (string, error) { func GetLibHome() (string, error) { return "", errors.New("homedir.GetLibHome() is not supported on this system") } + +// GetLibexecHome is unsupported on non-linux system. +func GetLibexecHome() (string, error) { + return "", errors.New("homedir.GetLibexecHome() is not supported on this system") +}