diff --git a/go.mod b/go.mod index 9c16b0c03..a75831948 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/containerd/go-cni v1.1.9-0.20230211172349-6603d5bd8941 github.com/containerd/go-runc v1.0.0 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/typeurl/v2 v2.1.0 github.com/containerd/zfs v1.0.0 diff --git a/go.sum b/go.sum index 4ffb2f522..3bbfef241 100644 --- a/go.sum +++ b/go.sum @@ -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-20210316161719-dbaa18c31c14/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.2.1-0.20230131001841-b3cabdec0657/go.mod h1:Q2u9Sudol4IkJ6YK0gShznKMxM6Un0Y3O4Wslf5Nerg= +github.com/containerd/nri v0.3.0 h1:2ZM4WImye1ypSnE7COjOvPAiLv84kaPILBDvb1tbDK8= +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/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= diff --git a/integration/client/go.sum b/integration/client/go.sum index 349d26fb7..d822bee30 100644 --- a/integration/client/go.sum +++ b/integration/client/go.sum @@ -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/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/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.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw= github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= diff --git a/pkg/nri/config.go b/pkg/nri/config.go index 06d31436a..a1cd5b1d2 100644 --- a/pkg/nri/config.go +++ b/pkg/nri/config.go @@ -17,6 +17,8 @@ package nri import ( + "time" + nri "github.com/containerd/nri/pkg/adaptation" ) @@ -24,35 +26,57 @@ import ( type Config struct { // Disable this NRI plugin and containerd NRI functionality altogether. 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 string `toml:"socket_path" json:"socketPath"` // PluginPath is the path to search for NRI plugins to launch on startup. 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. func DefaultConfig() *Config { return &Config{ - Disable: true, - ConfigPath: nri.DefaultConfigPath, - SocketPath: nri.DefaultSocketPath, - PluginPath: nri.DefaultPluginPath, + Disable: true, + SocketPath: nri.DefaultSocketPath, + PluginPath: nri.DefaultPluginPath, + PluginConfigPath: nri.DefaultPluginConfigPath, + + PluginRegistrationTimeout: nri.DefaultPluginRegistrationTimeout, + PluginRequestTimeout: nri.DefaultPluginRequestTimeout, } } // toOptions returns NRI options for this configuration. func (c *Config) toOptions() []nri.Option { opts := []nri.Option{} - if c.ConfigPath != "" { - opts = append(opts, nri.WithConfigPath(c.ConfigPath)) - } if c.SocketPath != "" { opts = append(opts, nri.WithSocketPath(c.SocketPath)) } if 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 } + +// 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) + } +} diff --git a/pkg/nri/nri.go b/pkg/nri/nri.go index 4323872a4..b8a66dcb7 100644 --- a/pkg/nri/nri.go +++ b/pkg/nri/nri.go @@ -123,6 +123,8 @@ func New(cfg *Config) (API, error) { err error ) + cfg.ConfigureTimeouts() + l.nri, err = nri.New(name, version, syncFn, updateFn, opts...) if err != nil { return nil, fmt.Errorf("failed to initialize NRI interface: %w", err) diff --git a/vendor/github.com/containerd/nri/pkg/adaptation/adaptation.go b/vendor/github.com/containerd/nri/pkg/adaptation/adaptation.go index eefa84940..441f6e1f0 100644 --- a/vendor/github.com/containerd/nri/pkg/adaptation/adaptation.go +++ b/vendor/github.com/containerd/nri/pkg/adaptation/adaptation.go @@ -32,12 +32,12 @@ import ( ) 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 = "/opt/nri/plugins" // DefaultSocketPath is the default socket path for external plugins. 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. @@ -54,12 +54,12 @@ type Adaptation struct { sync.Mutex name string version string - configPath string + dropinPath string pluginPath string socketPath string + dontListen bool syncFn SyncFn updateFn UpdateFn - cfg *Config listener net.Listener plugins []*plugin } @@ -72,23 +72,6 @@ var ( // Option to apply to the NRI runtime. 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. func WithPluginPath(path string) Option { 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. func WithSocketPath(path string) Option { 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. func New(name, version string, syncFn SyncFn, updateFn UpdateFn, opts ...Option) (*Adaptation, error) { var err error @@ -121,8 +120,8 @@ func New(name, version string, syncFn SyncFn, updateFn UpdateFn, opts ...Option) version: version, syncFn: syncFn, updateFn: updateFn, - configPath: DefaultConfigPath, pluginPath: DefaultPluginPath, + dropinPath: DefaultPluginConfigPath, 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") return r, nil @@ -374,13 +367,13 @@ func (r *Adaptation) removeClosedPlugins() { } func (r *Adaptation) startListener() error { - if r.cfg.DisableConnections { + if r.dontListen { log.Infof(noCtx, "connection from external plugins disabled") return nil } 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) } @@ -481,7 +474,7 @@ func (r *Adaptation) discoverPlugins() ([]string, []string, []string, error) { r.pluginPath, err) } - cfg, err := r.cfg.getPluginConfig(idx, base) + cfg, err := r.getPluginConfig(idx, base) if err != nil { return nil, nil, nil, fmt.Errorf("failed to discover plugins in %s: %w", r.pluginPath, err) diff --git a/vendor/github.com/containerd/nri/pkg/adaptation/config.go b/vendor/github.com/containerd/nri/pkg/adaptation/config.go deleted file mode 100644 index 049ea7bac..000000000 --- a/vendor/github.com/containerd/nri/pkg/adaptation/config.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/containerd/nri/pkg/adaptation/plugin.go b/vendor/github.com/containerd/nri/pkg/adaptation/plugin.go index 4043f244e..c4be402ea 100644 --- a/vendor/github.com/containerd/nri/pkg/adaptation/plugin.go +++ b/vendor/github.com/containerd/nri/pkg/adaptation/plugin.go @@ -36,8 +36,16 @@ import ( ) const ( - pluginRegistrationTimeout = 2 * time.Second - pluginRequestTimeout = 2 * time.Second + // DefaultPluginRegistrationTimeout is the default timeout for plugin registration. + 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 { @@ -59,6 +67,32 @@ type plugin struct { 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. func (r *Adaptation) newLaunchedPlugin(dir, idx, base, cfg string) (p *plugin, retErr error) { name := idx + "-" + base @@ -125,6 +159,27 @@ func (r *Adaptation) newExternalPlugin(conn stdnet.Conn) (p *plugin, retErr erro 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). func (p *plugin) isExternal() bool { 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. func (p *plugin) start(name, version string) error { - var err error + var ( + err error + timeout = getPluginRegistrationTimeout() + ) go func() { err := p.rpcs.Serve(context.Background(), p.rpcl) @@ -208,7 +266,7 @@ func (p *plugin) start(name, version string) error { } case <-p.closeC: return fmt.Errorf("failed to register plugin, connection closed") - case <-time.After(pluginRegistrationTimeout): + case <-time.After(timeout): p.close() p.stop() 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. 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() 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) { log.Infof(ctx, "synchronizing plugin %s", p.name()) - ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) + ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout()) defer cancel() req := &SynchronizeRequest{ @@ -368,7 +426,7 @@ func (p *plugin) createContainer(ctx context.Context, req *CreateContainerReques return nil, nil } - ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) + ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout()) defer cancel() rpl, err := p.stub.CreateContainer(ctx, req) @@ -391,7 +449,7 @@ func (p *plugin) updateContainer(ctx context.Context, req *UpdateContainerReques return nil, nil } - ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) + ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout()) defer cancel() rpl, err := p.stub.UpdateContainer(ctx, req) @@ -414,7 +472,7 @@ func (p *plugin) stopContainer(ctx context.Context, req *StopContainerRequest) ( return nil, nil } - ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) + ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout()) defer cancel() rpl, err := p.stub.StopContainer(ctx, req) @@ -437,7 +495,7 @@ func (p *plugin) StateChange(ctx context.Context, evt *StateChangeEvent) error { return nil } - ctx, cancel := context.WithTimeout(ctx, pluginRequestTimeout) + ctx, cancel := context.WithTimeout(ctx, getPluginRequestTimeout()) defer cancel() _, err := p.stub.StateChange(ctx, evt) diff --git a/vendor/github.com/containerd/nri/pkg/adaptation/result.go b/vendor/github.com/containerd/nri/pkg/adaptation/result.go index 7a7967006..a6188ef55 100644 --- a/vendor/github.com/containerd/nri/pkg/adaptation/result.go +++ b/vendor/github.com/containerd/nri/pkg/adaptation/result.go @@ -316,9 +316,7 @@ func (r *result) adjustMounts(mounts []*Mount, plugin string) error { } // finally, apply additions/modifications to plugin container creation request - for _, m := range add { - create.Container.Mounts = append(r.reply.adjust.Mounts, m) - } + create.Container.Mounts = append(create.Container.Mounts, add...) return nil } @@ -376,9 +374,7 @@ func (r *result) adjustDevices(devices []*LinuxDevice, plugin string) error { } // finally, apply additions/modifications to plugin container creation request - for _, d := range add { - create.Container.Linux.Devices = append(r.reply.adjust.Linux.Devices, d) - } + create.Container.Linux.Devices = append(create.Container.Linux.Devices, add...) return nil } diff --git a/vendor/github.com/containerd/nri/pkg/api/mount.go b/vendor/github.com/containerd/nri/pkg/api/mount.go index 63fd6956a..e35bf5b36 100644 --- a/vendor/github.com/containerd/nri/pkg/api/mount.go +++ b/vendor/github.com/containerd/nri/pkg/api/mount.go @@ -47,7 +47,6 @@ func (m *Mount) ToOCI(propagationQuery *string) rspec.Mount { Destination: m.Destination, Type: m.Type, Source: m.Source, - Options: []string{}, } for _, opt := range m.Options { o.Options = append(o.Options, opt) diff --git a/vendor/github.com/containerd/nri/pkg/api/plugin.go b/vendor/github.com/containerd/nri/pkg/api/plugin.go index 16aab20dc..c4fe8fcf9 100644 --- a/vendor/github.com/containerd/nri/pkg/api/plugin.go +++ b/vendor/github.com/containerd/nri/pkg/api/plugin.go @@ -23,7 +23,7 @@ import ( const ( // 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 = "NRI_PLUGIN_SOCKET" // PluginNameEnvVar is used to inform NRI-launched plugins about their name. diff --git a/vendor/github.com/containerd/nri/pkg/runtime-tools/generate/generate.go b/vendor/github.com/containerd/nri/pkg/runtime-tools/generate/generate.go index 81f4c1e7f..847383eb0 100644 --- a/vendor/github.com/containerd/nri/pkg/runtime-tools/generate/generate.go +++ b/vendor/github.com/containerd/nri/pkg/runtime-tools/generate/generate.go @@ -18,6 +18,9 @@ package generate import ( "fmt" + "os" + "path/filepath" + "sort" "strings" rspec "github.com/opencontainers/runtime-spec/specs-go" @@ -212,13 +215,27 @@ func (g *Generator) AdjustResources(r *nri.LinuxResources) error { g.initConfigLinux() if r.Cpu != nil { - g.SetLinuxResourcesCPUPeriod(r.Cpu.GetPeriod().GetValue()) - g.SetLinuxResourcesCPUQuota(r.Cpu.GetQuota().GetValue()) - g.SetLinuxResourcesCPUShares(r.Cpu.GetShares().GetValue()) - g.SetLinuxResourcesCPUCpus(r.Cpu.GetCpus()) - g.SetLinuxResourcesCPUMems(r.Cpu.GetMems()) - g.SetLinuxResourcesCPURealtimeRuntime(r.Cpu.GetRealtimeRuntime().GetValue()) - g.SetLinuxResourcesCPURealtimePeriod(r.Cpu.GetRealtimePeriod().GetValue()) + if r.Cpu.Period != nil { + g.SetLinuxResourcesCPUPeriod(r.Cpu.GetPeriod().GetValue()) + } + if r.Cpu.Quota != nil { + g.SetLinuxResourcesCPUQuota(r.Cpu.GetQuota().GetValue()) + } + if r.Cpu.Shares != nil { + g.SetLinuxResourcesCPUShares(r.Cpu.GetShares().GetValue()) + } + if r.Cpu.Cpus != "" { + g.SetLinuxResourcesCPUCpus(r.Cpu.GetCpus()) + } + if r.Cpu.Mems != "" { + g.SetLinuxResourcesCPUMems(r.Cpu.GetMems()) + } + if r.Cpu.RealtimeRuntime != nil { + g.SetLinuxResourcesCPURealtimeRuntime(r.Cpu.GetRealtimeRuntime().GetValue()) + } + if r.Cpu.RealtimePeriod != nil { + g.SetLinuxResourcesCPURealtimePeriod(r.Cpu.GetRealtimePeriod().GetValue()) + } } if r.Memory != nil { if l := r.Memory.GetLimit().GetValue(); l != 0 { @@ -305,19 +322,20 @@ func (g *Generator) AdjustDevices(devices []*nri.LinuxDevice) { // AdjustMounts adjusts the mounts in the OCI Spec. func (g *Generator) AdjustMounts(mounts []*nri.Mount) error { - var ( - propagation string - ) + if len(mounts) == 0 { + return nil + } + propagation := "" for _, m := range mounts { if destination, marked := m.IsMarkedForRemoval(); marked { g.RemoveMount(destination) continue } + g.RemoveMount(m.Destination) mnt := m.ToOCI(&propagation) - switch propagation { case "rprivate": case "rshared": @@ -340,10 +358,60 @@ func (g *Generator) AdjustMounts(mounts []*nri.Mount) error { } g.AddMount(mnt) } + g.sortMounts() 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) { return m, nil } diff --git a/vendor/github.com/containerd/nri/pkg/stub/stub.go b/vendor/github.com/containerd/nri/pkg/stub/stub.go index 39d6b22e6..470098657 100644 --- a/vendor/github.com/containerd/nri/pkg/stub/stub.go +++ b/vendor/github.com/containerd/nri/pkg/stub/stub.go @@ -18,6 +18,7 @@ package stub import ( "context" + "errors" "fmt" stdnet "net" "os" @@ -160,6 +161,10 @@ var ( // Used instead of a nil Context in logging. 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. @@ -515,6 +520,10 @@ func (stub *stub) connClosed() { // UpdateContainers requests unsolicited updates to containers. func (stub *stub) UpdateContainers(update []*api.ContainerUpdate) ([]*api.ContainerUpdate, error) { + if stub.runtime == nil { + return nil, ErrNoService + } + ctx := context.Background() req := &api.UpdateContainersRequest{ Update: update, diff --git a/vendor/modules.txt b/vendor/modules.txt index 470a7769c..810e9ec56 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -125,8 +125,8 @@ github.com/containerd/go-runc ## explicit; go 1.16 github.com/containerd/imgcrypt github.com/containerd/imgcrypt/images/encryption -# github.com/containerd/nri v0.2.1-0.20230131001841-b3cabdec0657 -## explicit; go 1.18 +# github.com/containerd/nri v0.3.0 +## explicit; go 1.19 github.com/containerd/nri github.com/containerd/nri/pkg/adaptation github.com/containerd/nri/pkg/api