From b2cb2e3aacaf66730b1fd1333203e0fe791e1e28 Mon Sep 17 00:00:00 2001 From: Tariq Ibrahim Date: Mon, 18 May 2026 14:08:04 -0700 Subject: [PATCH] nri refactor Signed-off-by: Tariq Ibrahim --- .../runtime/nri/cdi-device-injector.go | 81 +++++++++++++ .../container/runtime/nri/config.go | 67 +++++++++++ .../container/runtime/nri/config_test.go | 112 ++++++++++++++++++ .../container/runtime/nri/entry.go | 32 +++++ .../container/runtime/nri/manager.go | 69 +++++++++++ .../container/runtime/nri/manager_test.go | 57 +++++++++ .../container/runtime/nri/plugin.go | 91 +++----------- cmd/nvidia-ctk-installer/main.go | 57 ++++++--- 8 files changed, 472 insertions(+), 94 deletions(-) create mode 100644 cmd/nvidia-ctk-installer/container/runtime/nri/cdi-device-injector.go create mode 100644 cmd/nvidia-ctk-installer/container/runtime/nri/config.go create mode 100644 cmd/nvidia-ctk-installer/container/runtime/nri/config_test.go create mode 100644 cmd/nvidia-ctk-installer/container/runtime/nri/entry.go create mode 100644 cmd/nvidia-ctk-installer/container/runtime/nri/manager.go create mode 100644 cmd/nvidia-ctk-installer/container/runtime/nri/manager_test.go diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/cdi-device-injector.go b/cmd/nvidia-ctk-installer/container/runtime/nri/cdi-device-injector.go new file mode 100644 index 000000000..64890a982 --- /dev/null +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/cdi-device-injector.go @@ -0,0 +1,81 @@ +package nri + +import ( + "context" + "strings" + + "github.com/containerd/nri/pkg/api" + nrilog "github.com/containerd/nri/pkg/log" + "github.com/containerd/nri/pkg/plugin" + + "github.com/NVIDIA/nvidia-container-toolkit/internal/logger" +) + +type cdiInjectorPlugin struct { + logger nrilog.Logger + namespace string +} + +func NewCDIDeviceInjector(logger logger.Interface, namespace string) interface{} { + return &cdiInjectorPlugin{ + logger: toNriLogger{ + logger, + }, + namespace: namespace, + } +} + +// CreateContainer handles container creation requests. +func (c *cdiInjectorPlugin) CreateContainer(ctx context.Context, pod *api.PodSandbox, ctr *api.Container) (*api.ContainerAdjustment, []*api.ContainerUpdate, error) { + adjust := &api.ContainerAdjustment{} + + if err := c.injectCDIDevices(ctx, pod, ctr, adjust); err != nil { + return nil, nil, err + } + + return adjust, nil, nil +} + +func (c *cdiInjectorPlugin) injectCDIDevices(ctx context.Context, pod *api.PodSandbox, ctr *api.Container, a *api.ContainerAdjustment) error { + + devices := c.parseCDIDevices(ctx, pod, nriCDIAnnotationDomain, ctr.Name) + if len(devices) == 0 { + c.logger.Debugf(ctx, "%s: no CDI devices annotated...", containerName(pod, ctr)) + return nil + } + + c.logger.Infof(ctx, "%s: injecting CDI devices %v...", containerName(pod, ctr), devices) + for _, name := range devices { + a.AddCDIDevice( + &api.CDIDevice{ + Name: name, + }, + ) + } + + return nil +} + +// parseCDIDevices processes the podSpec and determines which containers which need CDI devices injected to them +func (c *cdiInjectorPlugin) parseCDIDevices(ctx context.Context, pod *api.PodSandbox, key, container string) []string { + if c.namespace != pod.Namespace { + c.logger.Debugf(ctx, "pod %s/%s is not in the toolkit's namespace %s. Skipping CDI device injection...", pod.Namespace, pod.Name, c.namespace) + return nil + } + + cdiDeviceNames, ok := plugin.GetEffectiveAnnotation(pod, key, container) + if !ok { + return nil + } + + cdiDevices := strings.Split(cdiDeviceNames, ",") + return cdiDevices +} + +// Construct a container name for log messages. +func containerName(pod *api.PodSandbox, container *api.Container) string { + if pod != nil { + return pod.Name + "/" + container.Name + } + return container.Name +} diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/config.go b/cmd/nvidia-ctk-installer/container/runtime/nri/config.go new file mode 100644 index 000000000..3ba4586d0 --- /dev/null +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/config.go @@ -0,0 +1,67 @@ +/** +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# 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 nri + +import ( + "fmt" +) + +const ( + maxPluginIndex = 99 +) + +// RegistrationConfig holds NRI registration settings shared by all plugins. +type RegistrationConfig struct { + // Index is the plugin index registered with NRI (0-99). + Index uint + // Socket is the path to the NRI socket. When empty, the NRI default is used. + Socket string +} + +// ValidateEntries checks that each entry is usable and that plugin indices are unique. +func ValidateEntries(entries []Entry) error { + if len(entries) == 0 { + return nil + } + + seenIndex := make(map[uint]string, len(entries)) + seenName := make(map[string]struct{}, len(entries)) + for i, entry := range entries { + if len(entry.Name) == 0 { + return fmt.Errorf("nri plugin %d: name must be specified", i) + } + if entry.PluginRunner == nil { + return fmt.Errorf("nri plugin %q: implementation must be specified", entry.Name) + } + if entry.Config.Index > maxPluginIndex { + return fmt.Errorf("nri plugin %q: index must be in the range [0,%d]", entry.Name, maxPluginIndex) + } + if other, ok := seenIndex[entry.Config.Index]; ok { + return fmt.Errorf("nri plugin %q: duplicate plugin index %d (already used by %q)", entry.Name, entry.Config.Index, other) + } + seenIndex[entry.Config.Index] = entry.Name + if _, ok := seenName[entry.Name]; ok { + return fmt.Errorf("nri plugin %q: duplicate plugin name", entry.Name) + } + seenName[entry.Name] = struct{}{} + } + return nil +} + +func (c RegistrationConfig) pluginIndex() string { + return fmt.Sprintf("%02d", c.Index) +} diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/config_test.go b/cmd/nvidia-ctk-installer/container/runtime/nri/config_test.go new file mode 100644 index 000000000..3baddd2ef --- /dev/null +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/config_test.go @@ -0,0 +1,112 @@ +/** +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# 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 nri + +import ( + "context" + "strings" + "testing" +) + +type stubRunner struct{} + +func (stubRunner) Start(context.Context, RegistrationConfig) error { return nil } +func (stubRunner) Stop() {} + +func TestValidateEntries(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + entries []Entry + wantErr string + }{ + { + name: "empty", + entries: nil, + }, + { + name: "valid single", + entries: []Entry{ + {Name: "management", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 10}}, + }, + }, + { + name: "valid multiple", + entries: []Entry{ + {Name: "management", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 10}}, + {Name: "other", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 11}}, + }, + }, + { + name: "missing name", + entries: []Entry{ + {PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 10}}, + }, + wantErr: "name must be specified", + }, + { + name: "missing runner", + entries: []Entry{ + {Name: "management", Config: RegistrationConfig{Index: 10}}, + }, + wantErr: "implementation must be specified", + }, + { + name: "index out of range", + entries: []Entry{ + {Name: "management", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 100}}, + }, + wantErr: "index must be in the range", + }, + { + name: "duplicate index", + entries: []Entry{ + {Name: "first", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 10}}, + {Name: "second", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 10}}, + }, + wantErr: "duplicate plugin index", + }, + { + name: "duplicate name", + entries: []Entry{ + {Name: "management", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 10}}, + {Name: "management", PluginRunner: stubRunner{}, Config: RegistrationConfig{Index: 11}}, + }, + wantErr: "duplicate plugin name", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := ValidateEntries(tc.entries) + if tc.wantErr == "" { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + return + } + if err == nil { + t.Fatal("expected error, got nil") + } + if !strings.Contains(err.Error(), tc.wantErr) { + t.Fatalf("error %q does not contain %q", err.Error(), tc.wantErr) + } + }) + } +} diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/entry.go b/cmd/nvidia-ctk-installer/container/runtime/nri/entry.go new file mode 100644 index 000000000..cb4f7a5ab --- /dev/null +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/entry.go @@ -0,0 +1,32 @@ +/** +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# 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 nri + +import "context" + +// Runner is an NRI plugin implementation that can be registered with the container runtime. +type Runner interface { + Start(ctx context.Context, cfg RegistrationConfig) error + Stop() +} + +// Entry associates a user-defined name and implementation with NRI registration settings. +type Entry struct { + Name string + Config RegistrationConfig + PluginRunner Runner +} diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/manager.go b/cmd/nvidia-ctk-installer/container/runtime/nri/manager.go new file mode 100644 index 000000000..770e9695a --- /dev/null +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/manager.go @@ -0,0 +1,69 @@ +/** +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# 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 nri + +import ( + "context" + "fmt" + + "github.com/NVIDIA/nvidia-container-toolkit/internal/logger" +) + +// Manager owns a set of running NRI plugin instances. +type Manager struct { + logger logger.Interface + plugins []Runner +} + +// NewManager creates a manager for starting and stopping NRI plugins. +func NewManager(log logger.Interface) *Manager { + return &Manager{ + logger: log, + } +} + +// Start initializes each entry. Already-started plugins are stopped if a later +// plugin fails to start. +func (m *Manager) Start(ctx context.Context, entries []Entry) error { + if err := ValidateEntries(entries); err != nil { + return err + } + if len(entries) == 0 { + return nil + } + + m.logger.Infof("Starting %d NRI plugin(s)...", len(entries)) + for _, entry := range entries { + m.logger.Infof("Starting NRI plugin %q (index=%02d, socket=%s)...", + entry.Name, entry.Config.Index, entry.Config.Socket) + + if err := entry.PluginRunner.Start(ctx, entry.Config); err != nil { + m.Stop() + return fmt.Errorf("nri plugin %q (index=%02d): %w", entry.Name, entry.Config.Index, err) + } + m.plugins = append(m.plugins, entry.PluginRunner) + } + return nil +} + +// Stop stops all running NRI plugins. +func (m *Manager) Stop() { + for _, plugin := range m.plugins { + plugin.Stop() + } + m.plugins = nil +} diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/manager_test.go b/cmd/nvidia-ctk-installer/container/runtime/nri/manager_test.go new file mode 100644 index 000000000..39c1661a0 --- /dev/null +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/manager_test.go @@ -0,0 +1,57 @@ +/** +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# 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 nri + +import ( + "context" + "sync/atomic" + "testing" + + "github.com/sirupsen/logrus" +) + +type countingRunner struct { + starts atomic.Int32 +} + +func (c *countingRunner) Start(context.Context, RegistrationConfig) error { + c.starts.Add(1) + return nil +} + +func (c *countingRunner) Stop() {} + +func TestManagerStartMultipleEntries(t *testing.T) { + t.Parallel() + + logger := logrus.New() + first := &countingRunner{} + second := &countingRunner{} + + manager := NewManager(logger) + entries := []Entry{ + {Name: "first", PluginRunner: first, Config: RegistrationConfig{Index: 10}}, + {Name: "second", PluginRunner: second, Config: RegistrationConfig{Index: 11}}, + } + if err := manager.Start(context.Background(), entries); err != nil { + t.Fatalf("Start: %v", err) + } + if first.starts.Load() != 1 || second.starts.Load() != 1 { + t.Fatalf("expected each runner to be started once") + } + manager.Stop() +} diff --git a/cmd/nvidia-ctk-installer/container/runtime/nri/plugin.go b/cmd/nvidia-ctk-installer/container/runtime/nri/plugin.go index f4cea8131..8ce4efec4 100644 --- a/cmd/nvidia-ctk-installer/container/runtime/nri/plugin.go +++ b/cmd/nvidia-ctk-installer/container/runtime/nri/plugin.go @@ -20,12 +20,9 @@ import ( "context" "fmt" "os" - "strings" "sync/atomic" "time" - "github.com/containerd/nri/pkg/api" - "github.com/containerd/nri/pkg/plugin" "github.com/containerd/nri/pkg/stub" "github.com/NVIDIA/nvidia-container-toolkit/internal/logger" @@ -45,11 +42,10 @@ const ( ) type Plugin struct { - ctx context.Context logger logger.Interface - namespace string - stub stub.Stub + pluginImpl interface{} + stub stub.Stub // stopped is set before Stop() so OnClose does not reconnect during shutdown. stopped atomic.Bool @@ -57,80 +53,23 @@ type Plugin struct { reconnectInProgress atomic.Bool } -// NewPlugin creates a new NRI plugin for injecting CDI devices -func NewPlugin(ctx context.Context, logger logger.Interface, namespace string) *Plugin { +// NewPlugin creates an NRI plugin that injects CDI devices from pod annotations. +func NewPlugin(logger logger.Interface, pluginImpl interface{}) *Plugin { return &Plugin{ - ctx: ctx, - logger: logger, - namespace: namespace, + logger: logger, + pluginImpl: pluginImpl, } } -// CreateContainer handles container creation requests. -func (p *Plugin) CreateContainer(_ context.Context, pod *api.PodSandbox, ctr *api.Container) (*api.ContainerAdjustment, []*api.ContainerUpdate, error) { - adjust := &api.ContainerAdjustment{} - - if err := p.injectCDIDevices(pod, ctr, adjust); err != nil { - return nil, nil, err - } - - return adjust, nil, nil -} - -func (p *Plugin) injectCDIDevices(pod *api.PodSandbox, ctr *api.Container, a *api.ContainerAdjustment) error { - ctx := p.ctx - pluginLogger := p.stub.Logger() - - devices := p.parseCDIDevices(pod, nriCDIAnnotationDomain, ctr.Name) - if len(devices) == 0 { - pluginLogger.Debugf(ctx, "%s: no CDI devices annotated...", containerName(pod, ctr)) - return nil - } - - pluginLogger.Infof(ctx, "%s: injecting CDI devices %v...", containerName(pod, ctr), devices) - for _, name := range devices { - a.AddCDIDevice( - &api.CDIDevice{ - Name: name, - }, - ) - } - - return nil -} - -// parseCDIDevices processes the podSpec and determines which containers which need CDI devices injected to them -func (p *Plugin) parseCDIDevices(pod *api.PodSandbox, key, container string) []string { - if p.namespace != pod.Namespace { - p.logger.Debugf("pod %s/%s is not in the toolkit's namespace %s. Skipping CDI device injection...", pod.Namespace, pod.Name, p.namespace) - return nil - } - - cdiDeviceNames, ok := plugin.GetEffectiveAnnotation(pod, key, container) - if !ok { - return nil - } - - cdiDevices := strings.Split(cdiDeviceNames, ",") - return cdiDevices -} - -// Construct a container name for log messages. -func containerName(pod *api.PodSandbox, container *api.Container) string { - if pod != nil { - return pod.Name + "/" + container.Name - } - return container.Name -} - -// Start starts the NRI plugin -func (p *Plugin) Start(ctx context.Context, nriSocketPath, nriPluginIdx string) error { +// Start starts the NRI plugin using the provided registration configuration. +func (p *Plugin) Start(ctx context.Context, cfg RegistrationConfig) error { + nriSocketPath := cfg.Socket pluginOpts := []stub.Option{ - stub.WithPluginIdx(nriPluginIdx), + stub.WithPluginIdx(cfg.pluginIndex()), stub.WithLogger(toNriLogger{p.logger}), stub.WithOnClose(func() { p.logger.Infof("NRI ttrpc connection to %s is down; attempting to reconnect...", nriSocketPath) - p.scheduleReconnect(nriSocketPath) + p.scheduleReconnect(ctx, nriSocketPath) }), } if len(nriSocketPath) > 0 { @@ -142,7 +81,7 @@ func (p *Plugin) Start(ctx context.Context, nriSocketPath, nriPluginIdx string) } var err error - if p.stub, err = stub.New(p, pluginOpts...); err != nil { + if p.stub, err = stub.New(p.pluginImpl, pluginOpts...); err != nil { return fmt.Errorf("failed to initialise plugin at %s: %w", nriSocketPath, err) } err = p.stub.Start(ctx) @@ -153,7 +92,7 @@ func (p *Plugin) Start(ctx context.Context, nriSocketPath, nriPluginIdx string) } // scheduleReconnect runs stub.Start in a loop until success, shutdown, or context cancellation. -func (p *Plugin) scheduleReconnect(nriSocketPath string) { +func (p *Plugin) scheduleReconnect(ctx context.Context, nriSocketPath string) { if !p.reconnectInProgress.CompareAndSwap(false, true) { return } @@ -165,12 +104,12 @@ func (p *Plugin) scheduleReconnect(nriSocketPath string) { return } select { - case <-p.ctx.Done(): + case <-ctx.Done(): return case <-time.After(nriReconnectBackoff): } p.logger.Infof("NRI plugin reconnecting to %s (attempt %d)...", nriSocketPath, i) - if err := p.stub.Start(p.ctx); err != nil { + if err := p.stub.Start(ctx); err != nil { p.logger.Warningf("NRI plugin reconnect failed: %v", err) if p.stopped.Load() { p.logger.Infof("NRI plugin stopped. Stopping all reconnect attempts...") diff --git a/cmd/nvidia-ctk-installer/main.go b/cmd/nvidia-ctk-installer/main.go index 0d9d96143..ac88ff5e7 100644 --- a/cmd/nvidia-ctk-installer/main.go +++ b/cmd/nvidia-ctk-installer/main.go @@ -51,6 +51,8 @@ type options struct { nriPluginIndex uint nriSocket string nriNamespace string + // nriPlugins holds additional named NRI plugins to start alongside the default plugin. + nriPlugins []nri.Entry toolkitOptions toolkit.Options @@ -65,6 +67,29 @@ func (o options) toolkitRoot() string { return filepath.Join(o.toolkitInstallDir, toolkitSubDir) } +func (o options) nriEnabled() bool { + return o.enableNRIPlugin || len(o.nriPlugins) > 0 +} + +const defaultNRIManagementPluginName = "cdi-device-injector" + +func (a *app) nriPluginEntries(o *options) []nri.Entry { + var entries []nri.Entry + if o.enableNRIPlugin { + cdiDeviceInjector := nri.NewCDIDeviceInjector(a.logger, o.nriNamespace) + entries = append(entries, nri.Entry{ + Name: defaultNRIManagementPluginName, + PluginRunner: nri.NewPlugin(a.logger, cdiDeviceInjector), + Config: nri.RegistrationConfig{ + Index: o.nriPluginIndex, + Socket: o.nriSocket, + }, + }) + } + entries = append(entries, o.nriPlugins...) + return entries +} + func main() { logger := logrus.New() c := NewApp(logger) @@ -249,6 +274,12 @@ func (a *app) validateFlags(c *cli.Command, o *options) error { return fmt.Errorf("the NRI namespace must be specified when the NRI plugin is enabled") } + if o.nriEnabled() { + if err := nri.ValidateEntries(a.nriPluginEntries(o)); err != nil { + return err + } + } + if err := a.toolkit.ValidateOptions(&o.toolkitOptions); err != nil { return err } @@ -270,9 +301,9 @@ func (a *app) Run(ctx context.Context, c *cli.Command, o *options) error { } defer a.shutdown(o.pidFile) - runtimeConfigurer := runtime.NewConfigurer(o.runtime, o.noRuntimeConfig, o.enableNRIPlugin) + runtimeConfigurer := runtime.NewConfigurer(o.runtime, o.noRuntimeConfig, o.nriEnabled()) - if o.enableNRIPlugin { + if o.nriEnabled() { // When NRI Plugin is enabled, the toolkit no longer interacts with the cri-o/containerd runtime config TOML and // therefore can no longer source the list of low-level runtime binary paths used by the container runtime. // This becomes an issue when migrating from non-NRI to NRI where the low-level runtime binary path previously @@ -308,12 +339,13 @@ func (a *app) Run(ctx context.Context, c *cli.Command, o *options) error { return nil } - if o.enableNRIPlugin { - nriPlugin, err := a.startNRIPluginServer(ctx, o) - if err != nil { - return fmt.Errorf("unable to start NRI plugin server: %w", err) + if o.nriEnabled() { + entries := a.nriPluginEntries(o) + nriManager := nri.NewManager(a.logger) + if err := nriManager.Start(ctx, entries); err != nil { + return fmt.Errorf("unable to start NRI plugins: %w", err) } - defer nriPlugin.Stop() + defer nriManager.Stop() } err = a.waitForSignal() @@ -380,17 +412,6 @@ func (a *app) waitForSignal() error { return nil } -func (a *app) startNRIPluginServer(ctx context.Context, opts *options) (*nri.Plugin, error) { - a.logger.Infof("Starting the NRI Plugin server....") - - plugin := nri.NewPlugin(ctx, a.logger, opts.nriNamespace) - err := plugin.Start(ctx, opts.nriSocket, fmt.Sprintf("%02d", opts.nriPluginIndex)) - if err != nil { - return nil, err - } - return plugin, nil -} - func (a *app) shutdown(pidFile string) { a.logger.Infof("Shutting Down")