Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 47 additions & 9 deletions cmd/nvidia-ctk-installer/container/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ func Flags(opts *Options) []cli.Flag {

// Validate checks whether the specified options are valid
func (opts *Options) Validate(logger logger.Interface, c *cli.Command, runtime string, toolkitRoot string, to *toolkit.Options) error {
// Check that the specified runtime is supported.
switch runtime {
case containerd.Name:
case crio.Name:
case docker.Name:
default:
return fmt.Errorf("invalid runtime %q; expected one of [containerd | crio | docker]", runtime)
}

// We set this option here to ensure that it is available in future calls.
opts.RuntimeDir = toolkitRoot

Expand Down Expand Up @@ -212,41 +221,70 @@ func (opts *Options) Validate(logger logger.Interface, c *cli.Command, runtime s
return nil
}

func Setup(c *cli.Command, opts *Options, runtime string) error {
switch runtime {
type Configurer interface {
Cleanup(*cli.Command, *Options) error
GetLowlevelRuntimePaths(*Options) ([]string, error)
Setup(*cli.Command, *Options) error
}

type runtime string
type noopRuntimeConfigurer struct{}

// NewConfigurer is a factory method for creating a runtime configurer.
func NewConfigurer(name string, noConfigureRuntime bool, nriEnabled bool) Configurer {
if noConfigureRuntime || nriEnabled {
return &noopRuntimeConfigurer{}
}
return runtime(name)
}

func (r runtime) Setup(c *cli.Command, opts *Options) error {
switch string(r) {
case containerd.Name:
return containerd.Setup(c, &opts.Options, &opts.containerdOptions)
case crio.Name:
return crio.Setup(c, &opts.Options, &opts.crioOptions)
case docker.Name:
return docker.Setup(c, &opts.Options)
default:
return fmt.Errorf("undefined runtime %v", runtime)
return fmt.Errorf("undefined runtime %v", r)
}
}

func Cleanup(c *cli.Command, opts *Options, runtime string) error {
switch runtime {
func (r runtime) Cleanup(c *cli.Command, opts *Options) error {
switch string(r) {
case containerd.Name:
return containerd.Cleanup(c, &opts.Options, &opts.containerdOptions)
case crio.Name:
return crio.Cleanup(c, &opts.Options, &opts.crioOptions)
case docker.Name:
return docker.Cleanup(c, &opts.Options)
default:
return fmt.Errorf("undefined runtime %v", runtime)
return fmt.Errorf("undefined runtime %v", r)
}
}

func GetLowlevelRuntimePaths(opts *Options, runtime string) ([]string, error) {
switch runtime {
func (r runtime) GetLowlevelRuntimePaths(opts *Options) ([]string, error) {
switch string(r) {
case containerd.Name:
return containerd.GetLowlevelRuntimePaths(&opts.Options, &opts.containerdOptions)
case crio.Name:
return crio.GetLowlevelRuntimePaths(&opts.Options)
case docker.Name:
return docker.GetLowlevelRuntimePaths(&opts.Options)
default:
return nil, fmt.Errorf("undefined runtime %v", runtime)
return nil, fmt.Errorf("undefined runtime %v", r)
}
}

func (r noopRuntimeConfigurer) Cleanup(_ *cli.Command, _ *Options) error {
return nil
}

func (r noopRuntimeConfigurer) GetLowlevelRuntimePaths(_ *Options) ([]string, error) {
return nil, nil
}

func (r noopRuntimeConfigurer) Setup(_ *cli.Command, _ *Options) error {
return nil
}
71 changes: 40 additions & 31 deletions cmd/nvidia-ctk-installer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const (
defaultNRIPluginIdx uint = 10
)

var availableRuntimes = map[string]struct{}{"docker": {}, "crio": {}, "containerd": {}}
var defaultLowLevelRuntimes = []string{"runc", "crun"}

var waitingForSignal = make(chan bool, 1)
Expand All @@ -44,7 +43,6 @@ type options struct {
toolkitInstallDir string

noDaemon bool
runtime string
pidFile string
sourceRoot string
packageType string
Expand All @@ -54,7 +52,10 @@ type options struct {
nriSocket string

toolkitOptions toolkit.Options
runtimeOptions runtime.Options

noRuntimeConfig bool
runtime string
runtimeOptions runtime.Options
}

func (o options) toolkitRoot() string {
Expand Down Expand Up @@ -113,6 +114,14 @@ func (a app) build() *cli.Command {
Destination: &options.noDaemon,
Sources: cli.EnvVars("NO_DAEMON"),
},
&cli.BoolFlag{
Name: "no-runtime-config",
Usage: "Disables the configuration of a container runtime. This is used in cases where the runtime has " +
"already been configured for use with the toolkit, and the installer is only used to deploy the " +
"components of the NVIDIA Container Toolkit.",
Destination: &options.noRuntimeConfig,
Sources: cli.EnvVars("NO_RUNTIME_CONFIG"),
},
&cli.BoolFlag{
Name: "enable-nri-plugin",
Aliases: []string{"p"},
Expand Down Expand Up @@ -142,9 +151,10 @@ func (a app) build() *cli.Command {
Sources: cli.EnvVars("NRI_SOCKET"),
},
&cli.StringFlag{
Name: "runtime",
Aliases: []string{"r"},
Usage: "the runtime to setup on this node. One of {'docker', 'crio', 'containerd'}",
Name: "runtime",
Aliases: []string{"r"},
Usage: "the runtime to setup on this node. One of {'docker', 'crio', 'containerd'}. " +
"This setting is ignored if --no-runtime-config is specified.",
Value: defaultRuntime,
Destination: &options.runtime,
Sources: cli.EnvVars("RUNTIME"),
Expand Down Expand Up @@ -212,19 +222,18 @@ func (a *app) validateFlags(c *cli.Command, o *options) error {
if o.toolkitInstallDir == "" {
return fmt.Errorf("the install root must be specified")
}
if _, exists := availableRuntimes[o.runtime]; !exists {
return fmt.Errorf("unknown runtime: %v", o.runtime)
}
if filepath.Base(o.pidFile) != toolkitPidFilename {
return fmt.Errorf("invalid toolkit.pid path %v", o.pidFile)
}

if err := a.toolkit.ValidateOptions(&o.toolkitOptions); err != nil {
return err
}

if err := o.runtimeOptions.Validate(a.logger, c, o.runtime, o.toolkitRoot(), &o.toolkitOptions); err != nil {
return err
}

return nil
}

Expand All @@ -238,8 +247,10 @@ 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)

if len(o.toolkitOptions.ContainerRuntimeRuntimes) == 0 {
lowlevelRuntimePaths, err := runtime.GetLowlevelRuntimePaths(&o.runtimeOptions, o.runtime)
lowlevelRuntimePaths, err := runtimeConfigurer.GetLowlevelRuntimePaths(&o.runtimeOptions)
if err != nil {
return fmt.Errorf("unable to determine runtime options: %w", err)
}
Expand All @@ -253,33 +264,31 @@ func (a *app) Run(ctx context.Context, c *cli.Command, o *options) error {
return fmt.Errorf("unable to install toolkit: %v", err)
}

if !o.enableNRIPlugin {
err = runtime.Setup(c, &o.runtimeOptions, o.runtime)
if err != nil {
return fmt.Errorf("unable to setup runtime: %w", err)
}
err = runtimeConfigurer.Setup(c, &o.runtimeOptions)
if err != nil {
return fmt.Errorf("unable to setup runtime: %w", err)
}

if !o.noDaemon {
if o.enableNRIPlugin {
nriPlugin, err := a.startNRIPluginServer(ctx, o)
if err != nil {
a.logger.Errorf("unable to start NRI plugin server: %v", err)
}
defer nriPlugin.Stop()
}
if o.noDaemon {
return nil
}

err = a.waitForSignal()
if o.enableNRIPlugin {
nriPlugin, err := a.startNRIPluginServer(ctx, o)
if err != nil {
return fmt.Errorf("unable to wait for signal: %v", err)
a.logger.Errorf("unable to start NRI plugin server: %v", err)
}
defer nriPlugin.Stop()
}

if !o.enableNRIPlugin {
err = runtime.Cleanup(c, &o.runtimeOptions, o.runtime)
if err != nil {
return fmt.Errorf("unable to cleanup runtime: %v", err)
}
}
err = a.waitForSignal()
if err != nil {
return fmt.Errorf("unable to wait for signal: %v", err)
}

err = runtimeConfigurer.Cleanup(c, &o.runtimeOptions)
if err != nil {
return fmt.Errorf("unable to cleanup runtime: %v", err)
}

return nil
Expand Down