pkg/nri: update NRI configuration.

Update NRI plugin configuration to match that of NRI. Remove
option for the eliminated NRI configuration file. Add option
to disable connections from externally launched plugins. Add
options to override default plugin registration and request
timeouts.

Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
This commit is contained in:
Krisztian Litkey 2023-02-06 20:05:59 +02:00
parent a18709442b
commit 310be5ce6e
14 changed files with 224 additions and 170 deletions

2
go.mod
View File

@ -17,7 +17,7 @@ require (
github.com/containerd/go-cni v1.1.9-0.20230211172349-6603d5bd8941 github.com/containerd/go-cni v1.1.9-0.20230211172349-6603d5bd8941
github.com/containerd/go-runc v1.0.0 github.com/containerd/go-runc v1.0.0
github.com/containerd/imgcrypt v1.1.5-0.20220421044638-8ba028dca028 github.com/containerd/imgcrypt v1.1.5-0.20220421044638-8ba028dca028
github.com/containerd/nri v0.2.1-0.20230131001841-b3cabdec0657 github.com/containerd/nri v0.3.0
github.com/containerd/ttrpc v1.1.1-0.20230127163717-32fab2374638 github.com/containerd/ttrpc v1.1.1-0.20230127163717-32fab2374638
github.com/containerd/typeurl/v2 v2.1.0 github.com/containerd/typeurl/v2 v2.1.0
github.com/containerd/zfs v1.0.0 github.com/containerd/zfs v1.0.0

4
go.sum
View File

@ -263,8 +263,8 @@ github.com/containerd/imgcrypt v1.1.5-0.20220421044638-8ba028dca028/go.mod h1:Lo
github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
github.com/containerd/nri v0.2.1-0.20230131001841-b3cabdec0657 h1:mUUkDOlFTZXCQupsMlHbQflPVxbj1sT6YOSz/hvT4wE= github.com/containerd/nri v0.3.0 h1:2ZM4WImye1ypSnE7COjOvPAiLv84kaPILBDvb1tbDK8=
github.com/containerd/nri v0.2.1-0.20230131001841-b3cabdec0657/go.mod h1:Q2u9Sudol4IkJ6YK0gShznKMxM6Un0Y3O4Wslf5Nerg= github.com/containerd/nri v0.3.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI=
github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=

View File

@ -520,7 +520,7 @@ github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0Z
github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0= github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/imgcrypt v1.1.5-0.20220421044638-8ba028dca028/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo= github.com/containerd/imgcrypt v1.1.5-0.20220421044638-8ba028dca028/go.mod h1:LorQnPtzL/T0IyCeftcsMEO7AqxUDbdO8j/tSUpgxvo=
github.com/containerd/nri v0.2.1-0.20230131001841-b3cabdec0657/go.mod h1:Q2u9Sudol4IkJ6YK0gShznKMxM6Un0Y3O4Wslf5Nerg= github.com/containerd/nri v0.3.0/go.mod h1:Zw9q2lP16sdg0zYybemZ9yTDy8g7fPCIB3KXOGlggXI=
github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw= github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=

View File

@ -17,6 +17,8 @@
package nri package nri
import ( import (
"time"
nri "github.com/containerd/nri/pkg/adaptation" nri "github.com/containerd/nri/pkg/adaptation"
) )
@ -24,35 +26,57 @@ import (
type Config struct { type Config struct {
// Disable this NRI plugin and containerd NRI functionality altogether. // Disable this NRI plugin and containerd NRI functionality altogether.
Disable bool `toml:"disable" json:"disable"` Disable bool `toml:"disable" json:"disable"`
// ConfigPath is the path to the NRI configuration file to use.
ConfigPath string `toml:"config_file" json:"configFile"`
// SocketPath is the path to the NRI socket to create for NRI plugins to connect to. // SocketPath is the path to the NRI socket to create for NRI plugins to connect to.
SocketPath string `toml:"socket_path" json:"socketPath"` SocketPath string `toml:"socket_path" json:"socketPath"`
// PluginPath is the path to search for NRI plugins to launch on startup. // PluginPath is the path to search for NRI plugins to launch on startup.
PluginPath string `toml:"plugin_path" json:"pluginPath"` PluginPath string `toml:"plugin_path" json:"pluginPath"`
// PluginConfigPath is the path to search for plugin-specific configuration.
PluginConfigPath string `toml:"plugin_config_path" json:"pluginConfigPath"`
// PluginRegistrationTimeout is the timeout for plugin registration.
PluginRegistrationTimeout time.Duration `toml:"plugin_registration_timeout" json:"pluginRegistrationTimeout"`
// PluginRequestTimeout is the timeout for a plugin to handle a request.
PluginRequestTimeout time.Duration `toml:"plugin_request_timeout" json:"pluginRequestTimeout"`
// DisableConnections disables connections from externally launched plugins.
DisableConnections bool `toml:"disable_connections" json:"disableConnections"`
} }
// DefaultConfig returns the default configuration. // DefaultConfig returns the default configuration.
func DefaultConfig() *Config { func DefaultConfig() *Config {
return &Config{ return &Config{
Disable: true, Disable: true,
ConfigPath: nri.DefaultConfigPath,
SocketPath: nri.DefaultSocketPath, SocketPath: nri.DefaultSocketPath,
PluginPath: nri.DefaultPluginPath, PluginPath: nri.DefaultPluginPath,
PluginConfigPath: nri.DefaultPluginConfigPath,
PluginRegistrationTimeout: nri.DefaultPluginRegistrationTimeout,
PluginRequestTimeout: nri.DefaultPluginRequestTimeout,
} }
} }
// toOptions returns NRI options for this configuration. // toOptions returns NRI options for this configuration.
func (c *Config) toOptions() []nri.Option { func (c *Config) toOptions() []nri.Option {
opts := []nri.Option{} opts := []nri.Option{}
if c.ConfigPath != "" {
opts = append(opts, nri.WithConfigPath(c.ConfigPath))
}
if c.SocketPath != "" { if c.SocketPath != "" {
opts = append(opts, nri.WithSocketPath(c.SocketPath)) opts = append(opts, nri.WithSocketPath(c.SocketPath))
} }
if c.PluginPath != "" { if c.PluginPath != "" {
opts = append(opts, nri.WithPluginPath(c.PluginPath)) opts = append(opts, nri.WithPluginPath(c.PluginPath))
} }
if c.PluginConfigPath != "" {
opts = append(opts, nri.WithPluginConfigPath(c.PluginConfigPath))
}
if c.DisableConnections {
opts = append(opts, nri.WithDisabledExternalConnections())
}
return opts return opts
} }
// ConfigureTimeouts sets timeout options for NRI.
func (c *Config) ConfigureTimeouts() {
if c.PluginRegistrationTimeout != 0 {
nri.SetPluginRegistrationTimeout(c.PluginRegistrationTimeout)
}
if c.PluginRequestTimeout != 0 {
nri.SetPluginRequestTimeout(c.PluginRequestTimeout)
}
}

View File

@ -123,6 +123,8 @@ func New(cfg *Config) (API, error) {
err error err error
) )
cfg.ConfigureTimeouts()
l.nri, err = nri.New(name, version, syncFn, updateFn, opts...) l.nri, err = nri.New(name, version, syncFn, updateFn, opts...)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to initialize NRI interface: %w", err) return nil, fmt.Errorf("failed to initialize NRI interface: %w", err)

View File

@ -32,12 +32,12 @@ import (
) )
const ( const (
// DefaultConfigPath is the default path to the NRI configuration.
DefaultConfigPath = "/etc/nri/nri.conf"
// DefaultPluginPath is the default path to search for NRI plugins. // DefaultPluginPath is the default path to search for NRI plugins.
DefaultPluginPath = "/opt/nri/plugins" DefaultPluginPath = "/opt/nri/plugins"
// DefaultSocketPath is the default socket path for external plugins. // DefaultSocketPath is the default socket path for external plugins.
DefaultSocketPath = api.DefaultSocketPath DefaultSocketPath = api.DefaultSocketPath
// PluginConfigDir is the drop-in directory for NRI-launched plugin configuration.
DefaultPluginConfigPath = "/etc/nri/conf.d"
) )
// SyncFn is a container runtime function for state synchronization. // SyncFn is a container runtime function for state synchronization.
@ -54,12 +54,12 @@ type Adaptation struct {
sync.Mutex sync.Mutex
name string name string
version string version string
configPath string dropinPath string
pluginPath string pluginPath string
socketPath string socketPath string
dontListen bool
syncFn SyncFn syncFn SyncFn
updateFn UpdateFn updateFn UpdateFn
cfg *Config
listener net.Listener listener net.Listener
plugins []*plugin plugins []*plugin
} }
@ -72,23 +72,6 @@ var (
// Option to apply to the NRI runtime. // Option to apply to the NRI runtime.
type Option func(*Adaptation) error type Option func(*Adaptation) error
// WithConfigPath returns an option to override the default NRI config path.
func WithConfigPath(path string) Option {
return func(r *Adaptation) error {
r.configPath = path
return nil
}
}
// WithConfig returns an option to provide a pre-parsed NRI configuration.
func WithConfig(cfg *Config) Option {
return func(r *Adaptation) error {
r.cfg = cfg
r.configPath = cfg.path
return nil
}
}
// WithPluginPath returns an option to override the default NRI plugin path. // WithPluginPath returns an option to override the default NRI plugin path.
func WithPluginPath(path string) Option { func WithPluginPath(path string) Option {
return func(r *Adaptation) error { return func(r *Adaptation) error {
@ -97,6 +80,14 @@ func WithPluginPath(path string) Option {
} }
} }
// WithPluginConfigPath returns an option to override the default NRI plugin config path.
func WithPluginConfigPath(path string) Option {
return func(r *Adaptation) error {
r.dropinPath = path
return nil
}
}
// WithSocketPath returns an option to override the default NRI socket path. // WithSocketPath returns an option to override the default NRI socket path.
func WithSocketPath(path string) Option { func WithSocketPath(path string) Option {
return func(r *Adaptation) error { return func(r *Adaptation) error {
@ -105,6 +96,14 @@ func WithSocketPath(path string) Option {
} }
} }
// WithDisabledExternalConnections returns an options to disable accepting plugin connections.
func WithDisabledExternalConnections() Option {
return func(r *Adaptation) error {
r.dontListen = true
return nil
}
}
// New creates a new NRI Runtime. // New creates a new NRI Runtime.
func New(name, version string, syncFn SyncFn, updateFn UpdateFn, opts ...Option) (*Adaptation, error) { func New(name, version string, syncFn SyncFn, updateFn UpdateFn, opts ...Option) (*Adaptation, error) {
var err error var err error
@ -121,8 +120,8 @@ func New(name, version string, syncFn SyncFn, updateFn UpdateFn, opts ...Option)
version: version, version: version,
syncFn: syncFn, syncFn: syncFn,
updateFn: updateFn, updateFn: updateFn,
configPath: DefaultConfigPath,
pluginPath: DefaultPluginPath, pluginPath: DefaultPluginPath,
dropinPath: DefaultPluginConfigPath,
socketPath: DefaultSocketPath, socketPath: DefaultSocketPath,
} }
@ -132,12 +131,6 @@ func New(name, version string, syncFn SyncFn, updateFn UpdateFn, opts ...Option)
} }
} }
if r.cfg == nil {
if r.cfg, err = ReadConfig(r.configPath); err != nil {
return nil, err
}
}
log.Infof(noCtx, "runtime interface created") log.Infof(noCtx, "runtime interface created")
return r, nil return r, nil
@ -374,13 +367,13 @@ func (r *Adaptation) removeClosedPlugins() {
} }
func (r *Adaptation) startListener() error { func (r *Adaptation) startListener() error {
if r.cfg.DisableConnections { if r.dontListen {
log.Infof(noCtx, "connection from external plugins disabled") log.Infof(noCtx, "connection from external plugins disabled")
return nil return nil
} }
os.Remove(r.socketPath) os.Remove(r.socketPath)
if err := os.MkdirAll(filepath.Dir(r.socketPath), 0755); err != nil { if err := os.MkdirAll(filepath.Dir(r.socketPath), 0700); err != nil {
return fmt.Errorf("failed to create socket %q: %w", r.socketPath, err) return fmt.Errorf("failed to create socket %q: %w", r.socketPath, err)
} }
@ -481,7 +474,7 @@ func (r *Adaptation) discoverPlugins() ([]string, []string, []string, error) {
r.pluginPath, err) r.pluginPath, err)
} }
cfg, err := r.cfg.getPluginConfig(idx, base) cfg, err := r.getPluginConfig(idx, base)
if err != nil { if err != nil {
return nil, nil, nil, fmt.Errorf("failed to discover plugins in %s: %w", return nil, nil, nil, fmt.Errorf("failed to discover plugins in %s: %w",
r.pluginPath, err) r.pluginPath, err)

View File

@ -1,95 +0,0 @@
/*
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 adaptation
import (
"fmt"
"os"
"path/filepath"
"sigs.k8s.io/yaml"
)
const (
// PluginConfigSubdir is the drop-in directory for plugin configuration.
PluginConfigSubdir = "conf.d"
)
// Config is the runtime configuration for NRI.
type Config struct {
// DisableConnections disables plugin-initiated connections.
DisableConnections bool `json:"disableConnections"`
path string
dropIn string
}
// DefaultConfig returns the default NRI configuration for a given path.
// This configuration should be identical to what ReadConfig would return
// for an empty file at the given location. If the given path is empty,
// DefaultConfigPath is used instead.
func DefaultConfig(path string) *Config {
if path == "" {
path = DefaultConfigPath
}
return &Config{
path: path,
dropIn: filepath.Join(filepath.Dir(path), PluginConfigSubdir),
}
}
// ReadConfig reads the NRI runtime configuration from a file.
func ReadConfig(path string) (*Config, error) {
buf, err := os.ReadFile(path)
if os.IsNotExist(err) {
return nil, err
}
if err != nil {
return nil, fmt.Errorf("failed to read file %q: %w", path, err)
}
cfg := &Config{}
err = yaml.UnmarshalStrict(buf, cfg)
if err != nil {
return nil, fmt.Errorf("failed to parse file %q: %w", path, err)
}
cfg.path = path
cfg.dropIn = filepath.Join(filepath.Dir(path), PluginConfigSubdir)
return cfg, nil
}
func (cfg *Config) getPluginConfig(id, base string) (string, error) {
name := id + "-" + base
dropIns := []string{
filepath.Join(cfg.dropIn, name+".conf"),
filepath.Join(cfg.dropIn, base+".conf"),
}
for _, path := range dropIns {
buf, err := os.ReadFile(path)
if err == nil {
return string(buf), nil
}
if !os.IsNotExist(err) {
return "", fmt.Errorf("failed to read configuration for plugin %q: %w", name, err)
}
}
return "", nil
}

View File

@ -36,8 +36,16 @@ import (
) )
const ( const (
pluginRegistrationTimeout = 2 * time.Second // DefaultPluginRegistrationTimeout is the default timeout for plugin registration.
pluginRequestTimeout = 2 * time.Second DefaultPluginRegistrationTimeout = 5 * time.Second
// DefaultPluginRequestTimeout is the default timeout for plugins to handle a request.
DefaultPluginRequestTimeout = 2 * time.Second
)
var (
pluginRegistrationTimeout = DefaultPluginRegistrationTimeout
pluginRequestTimeout = DefaultPluginRequestTimeout
timeoutCfgLock sync.RWMutex
) )
type plugin struct { type plugin struct {
@ -59,6 +67,32 @@ type plugin struct {
r *Adaptation r *Adaptation
} }
// SetPluginRegistrationTimeout sets the timeout for plugin registration.
func SetPluginRegistrationTimeout(t time.Duration) {
timeoutCfgLock.Lock()
defer timeoutCfgLock.Unlock()
pluginRegistrationTimeout = t
}
func getPluginRegistrationTimeout() time.Duration {
timeoutCfgLock.RLock()
defer timeoutCfgLock.RUnlock()
return pluginRegistrationTimeout
}
// SetPluginRequestTimeout sets the timeout for plugins to handle a request.
func SetPluginRequestTimeout(t time.Duration) {
timeoutCfgLock.Lock()
defer timeoutCfgLock.Unlock()
pluginRequestTimeout = t
}
func getPluginRequestTimeout() time.Duration {
timeoutCfgLock.RLock()
defer timeoutCfgLock.RUnlock()
return pluginRequestTimeout
}
// Launch a pre-installed plugin with a pre-connected socketpair. // Launch a pre-installed plugin with a pre-connected socketpair.
func (r *Adaptation) newLaunchedPlugin(dir, idx, base, cfg string) (p *plugin, retErr error) { func (r *Adaptation) newLaunchedPlugin(dir, idx, base, cfg string) (p *plugin, retErr error) {
name := idx + "-" + base name := idx + "-" + base
@ -125,6 +159,27 @@ func (r *Adaptation) newExternalPlugin(conn stdnet.Conn) (p *plugin, retErr erro
return p, nil return p, nil
} }
// Get plugin-specific configuration for an NRI-launched plugin.
func (r *Adaptation) getPluginConfig(id, base string) (string, error) {
name := id + "-" + base
dropIns := []string{
filepath.Join(r.dropinPath, name+".conf"),
filepath.Join(r.dropinPath, base+".conf"),
}
for _, path := range dropIns {
buf, err := os.ReadFile(path)
if err == nil {
return string(buf), nil
}
if !os.IsNotExist(err) {
return "", fmt.Errorf("failed to read configuration for plugin %q: %w", name, err)
}
}
return "", nil
}
// Check if the plugin is external (was not launched by us). // Check if the plugin is external (was not launched by us).
func (p *plugin) isExternal() bool { func (p *plugin) isExternal() bool {
return p.cmd == nil return p.cmd == nil
@ -189,7 +244,10 @@ func (p *plugin) connect(conn stdnet.Conn) (retErr error) {
// Start Runtime service, wait for plugin to register, then configure it. // Start Runtime service, wait for plugin to register, then configure it.
func (p *plugin) start(name, version string) error { func (p *plugin) start(name, version string) error {
var err error var (
err error
timeout = getPluginRegistrationTimeout()
)
go func() { go func() {
err := p.rpcs.Serve(context.Background(), p.rpcl) err := p.rpcs.Serve(context.Background(), p.rpcl)
@ -208,7 +266,7 @@ func (p *plugin) start(name, version string) error {
} }
case <-p.closeC: case <-p.closeC:
return fmt.Errorf("failed to register plugin, connection closed") return fmt.Errorf("failed to register plugin, connection closed")
case <-time.After(pluginRegistrationTimeout): case <-time.After(timeout):
p.close() p.close()
p.stop() p.stop()
return errors.New("plugin registration timed out") return errors.New("plugin registration timed out")
@ -318,7 +376,7 @@ func (p *plugin) UpdateContainers(ctx context.Context, req *UpdateContainersRequ
// configure the plugin and subscribe it for the events it requested. // configure the plugin and subscribe it for the events it requested.
func (p *plugin) configure(ctx context.Context, name, version, config string) error { func (p *plugin) configure(ctx context.Context, name, version, config string) error {
ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout())
defer cancel() defer cancel()
rpl, err := p.stub.Configure(ctx, &ConfigureRequest{ rpl, err := p.stub.Configure(ctx, &ConfigureRequest{
@ -347,7 +405,7 @@ func (p *plugin) configure(ctx context.Context, name, version, config string) er
func (p *plugin) synchronize(ctx context.Context, pods []*PodSandbox, containers []*Container) ([]*ContainerUpdate, error) { func (p *plugin) synchronize(ctx context.Context, pods []*PodSandbox, containers []*Container) ([]*ContainerUpdate, error) {
log.Infof(ctx, "synchronizing plugin %s", p.name()) log.Infof(ctx, "synchronizing plugin %s", p.name())
ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout())
defer cancel() defer cancel()
req := &SynchronizeRequest{ req := &SynchronizeRequest{
@ -368,7 +426,7 @@ func (p *plugin) createContainer(ctx context.Context, req *CreateContainerReques
return nil, nil return nil, nil
} }
ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout())
defer cancel() defer cancel()
rpl, err := p.stub.CreateContainer(ctx, req) rpl, err := p.stub.CreateContainer(ctx, req)
@ -391,7 +449,7 @@ func (p *plugin) updateContainer(ctx context.Context, req *UpdateContainerReques
return nil, nil return nil, nil
} }
ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout())
defer cancel() defer cancel()
rpl, err := p.stub.UpdateContainer(ctx, req) rpl, err := p.stub.UpdateContainer(ctx, req)
@ -414,7 +472,7 @@ func (p *plugin) stopContainer(ctx context.Context, req *StopContainerRequest) (
return nil, nil return nil, nil
} }
ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout())
defer cancel() defer cancel()
rpl, err := p.stub.StopContainer(ctx, req) rpl, err := p.stub.StopContainer(ctx, req)
@ -437,7 +495,7 @@ func (p *plugin) StateChange(ctx context.Context, evt *StateChangeEvent) error {
return nil return nil
} }
ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout())
defer cancel() defer cancel()
_, err := p.stub.StateChange(ctx, evt) _, err := p.stub.StateChange(ctx, evt)

View File

@ -316,9 +316,7 @@ func (r *result) adjustMounts(mounts []*Mount, plugin string) error {
} }
// finally, apply additions/modifications to plugin container creation request // finally, apply additions/modifications to plugin container creation request
for _, m := range add { create.Container.Mounts = append(create.Container.Mounts, add...)
create.Container.Mounts = append(r.reply.adjust.Mounts, m)
}
return nil return nil
} }
@ -376,9 +374,7 @@ func (r *result) adjustDevices(devices []*LinuxDevice, plugin string) error {
} }
// finally, apply additions/modifications to plugin container creation request // finally, apply additions/modifications to plugin container creation request
for _, d := range add { create.Container.Linux.Devices = append(create.Container.Linux.Devices, add...)
create.Container.Linux.Devices = append(r.reply.adjust.Linux.Devices, d)
}
return nil return nil
} }

View File

@ -47,7 +47,6 @@ func (m *Mount) ToOCI(propagationQuery *string) rspec.Mount {
Destination: m.Destination, Destination: m.Destination,
Type: m.Type, Type: m.Type,
Source: m.Source, Source: m.Source,
Options: []string{},
} }
for _, opt := range m.Options { for _, opt := range m.Options {
o.Options = append(o.Options, opt) o.Options = append(o.Options, opt)

View File

@ -23,7 +23,7 @@ import (
const ( const (
// DefaultSocketPath is the default socket path for external plugins. // DefaultSocketPath is the default socket path for external plugins.
DefaultSocketPath = "/var/run/nri.sock" DefaultSocketPath = "/var/run/nri/nri.sock"
// PluginSocketEnvVar is used to inform plugins about pre-connected sockets. // PluginSocketEnvVar is used to inform plugins about pre-connected sockets.
PluginSocketEnvVar = "NRI_PLUGIN_SOCKET" PluginSocketEnvVar = "NRI_PLUGIN_SOCKET"
// PluginNameEnvVar is used to inform NRI-launched plugins about their name. // PluginNameEnvVar is used to inform NRI-launched plugins about their name.

View File

@ -18,6 +18,9 @@ package generate
import ( import (
"fmt" "fmt"
"os"
"path/filepath"
"sort"
"strings" "strings"
rspec "github.com/opencontainers/runtime-spec/specs-go" rspec "github.com/opencontainers/runtime-spec/specs-go"
@ -212,14 +215,28 @@ func (g *Generator) AdjustResources(r *nri.LinuxResources) error {
g.initConfigLinux() g.initConfigLinux()
if r.Cpu != nil { if r.Cpu != nil {
if r.Cpu.Period != nil {
g.SetLinuxResourcesCPUPeriod(r.Cpu.GetPeriod().GetValue()) g.SetLinuxResourcesCPUPeriod(r.Cpu.GetPeriod().GetValue())
}
if r.Cpu.Quota != nil {
g.SetLinuxResourcesCPUQuota(r.Cpu.GetQuota().GetValue()) g.SetLinuxResourcesCPUQuota(r.Cpu.GetQuota().GetValue())
}
if r.Cpu.Shares != nil {
g.SetLinuxResourcesCPUShares(r.Cpu.GetShares().GetValue()) g.SetLinuxResourcesCPUShares(r.Cpu.GetShares().GetValue())
}
if r.Cpu.Cpus != "" {
g.SetLinuxResourcesCPUCpus(r.Cpu.GetCpus()) g.SetLinuxResourcesCPUCpus(r.Cpu.GetCpus())
}
if r.Cpu.Mems != "" {
g.SetLinuxResourcesCPUMems(r.Cpu.GetMems()) g.SetLinuxResourcesCPUMems(r.Cpu.GetMems())
}
if r.Cpu.RealtimeRuntime != nil {
g.SetLinuxResourcesCPURealtimeRuntime(r.Cpu.GetRealtimeRuntime().GetValue()) g.SetLinuxResourcesCPURealtimeRuntime(r.Cpu.GetRealtimeRuntime().GetValue())
}
if r.Cpu.RealtimePeriod != nil {
g.SetLinuxResourcesCPURealtimePeriod(r.Cpu.GetRealtimePeriod().GetValue()) g.SetLinuxResourcesCPURealtimePeriod(r.Cpu.GetRealtimePeriod().GetValue())
} }
}
if r.Memory != nil { if r.Memory != nil {
if l := r.Memory.GetLimit().GetValue(); l != 0 { if l := r.Memory.GetLimit().GetValue(); l != 0 {
g.SetLinuxResourcesMemoryLimit(l) g.SetLinuxResourcesMemoryLimit(l)
@ -305,19 +322,20 @@ func (g *Generator) AdjustDevices(devices []*nri.LinuxDevice) {
// AdjustMounts adjusts the mounts in the OCI Spec. // AdjustMounts adjusts the mounts in the OCI Spec.
func (g *Generator) AdjustMounts(mounts []*nri.Mount) error { func (g *Generator) AdjustMounts(mounts []*nri.Mount) error {
var ( if len(mounts) == 0 {
propagation string return nil
) }
propagation := ""
for _, m := range mounts { for _, m := range mounts {
if destination, marked := m.IsMarkedForRemoval(); marked { if destination, marked := m.IsMarkedForRemoval(); marked {
g.RemoveMount(destination) g.RemoveMount(destination)
continue continue
} }
g.RemoveMount(m.Destination) g.RemoveMount(m.Destination)
mnt := m.ToOCI(&propagation) mnt := m.ToOCI(&propagation)
switch propagation { switch propagation {
case "rprivate": case "rprivate":
case "rshared": case "rshared":
@ -340,10 +358,60 @@ func (g *Generator) AdjustMounts(mounts []*nri.Mount) error {
} }
g.AddMount(mnt) g.AddMount(mnt)
} }
g.sortMounts()
return nil return nil
} }
// sortMounts sorts the mounts in the generated OCI Spec.
func (g *Generator) sortMounts() {
mounts := g.Generator.Mounts()
g.Generator.ClearMounts()
sort.Sort(orderedMounts(mounts))
// TODO(klihub): This is now a bit ugly maybe we should introduce a
// SetMounts([]rspec.Mount) to runtime-tools/generate.Generator. That
// could also take care of properly sorting the mount slice.
g.Generator.Config.Mounts = mounts
}
// orderedMounts defines how to sort an OCI Spec Mount slice.
// This is the almost the same implementation sa used by CRI-O and Docker,
// with a minor tweak for stable sorting order (easier to test):
//
// https://github.com/moby/moby/blob/17.05.x/daemon/volumes.go#L26
type orderedMounts []rspec.Mount
// Len returns the number of mounts. Used in sorting.
func (m orderedMounts) Len() int {
return len(m)
}
// Less returns true if the number of parts (a/b/c would be 3 parts) in the
// mount indexed by parameter 1 is less than that of the mount indexed by
// parameter 2. Used in sorting.
func (m orderedMounts) Less(i, j int) bool {
ip, jp := m.parts(i), m.parts(j)
if ip < jp {
return true
}
if jp < ip {
return false
}
return m[i].Destination < m[j].Destination
}
// Swap swaps two items in an array of mounts. Used in sorting
func (m orderedMounts) Swap(i, j int) {
m[i], m[j] = m[j], m[i]
}
// parts returns the number of parts in the destination of a mount. Used in sorting.
func (m orderedMounts) parts(i int) int {
return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
}
func nopFilter(m map[string]string) (map[string]string, error) { func nopFilter(m map[string]string) (map[string]string, error) {
return m, nil return m, nil
} }

View File

@ -18,6 +18,7 @@ package stub
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
stdnet "net" stdnet "net"
"os" "os"
@ -160,6 +161,10 @@ var (
// Used instead of a nil Context in logging. // Used instead of a nil Context in logging.
noCtx = context.TODO() noCtx = context.TODO()
// ErrNoService indicates that the stub has no runtime service/connection,
// for instance by UpdateContainers on a stub which has not been started.
ErrNoService = errors.New("stub: no service/connection")
) )
// EventMask holds a mask of events for plugin subscription. // EventMask holds a mask of events for plugin subscription.
@ -515,6 +520,10 @@ func (stub *stub) connClosed() {
// UpdateContainers requests unsolicited updates to containers. // UpdateContainers requests unsolicited updates to containers.
func (stub *stub) UpdateContainers(update []*api.ContainerUpdate) ([]*api.ContainerUpdate, error) { func (stub *stub) UpdateContainers(update []*api.ContainerUpdate) ([]*api.ContainerUpdate, error) {
if stub.runtime == nil {
return nil, ErrNoService
}
ctx := context.Background() ctx := context.Background()
req := &api.UpdateContainersRequest{ req := &api.UpdateContainersRequest{
Update: update, Update: update,

4
vendor/modules.txt vendored
View File

@ -125,8 +125,8 @@ github.com/containerd/go-runc
## explicit; go 1.16 ## explicit; go 1.16
github.com/containerd/imgcrypt github.com/containerd/imgcrypt
github.com/containerd/imgcrypt/images/encryption github.com/containerd/imgcrypt/images/encryption
# github.com/containerd/nri v0.2.1-0.20230131001841-b3cabdec0657 # github.com/containerd/nri v0.3.0
## explicit; go 1.18 ## explicit; go 1.19
github.com/containerd/nri github.com/containerd/nri
github.com/containerd/nri/pkg/adaptation github.com/containerd/nri/pkg/adaptation
github.com/containerd/nri/pkg/api github.com/containerd/nri/pkg/api