NRI: instantiate and start/stop NRI adaptation

Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
Rob Murray
2025-12-02 17:26:12 +00:00
parent 4941b36883
commit 282868dabf
4 changed files with 151 additions and 1 deletions

View File

@@ -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
}