Skip to content

Commit 9b7904e

Browse files
author
Evan Lezar
committed
Merge branch 'CNT-4075' into 'release-1.13'
Allow same envars for all runtime configs See merge request nvidia/container-toolkit/container-toolkit!419
2 parents 7a70850 + a9ccef6 commit 9b7904e

File tree

9 files changed

+440
-535
lines changed

9 files changed

+440
-535
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## v1.13.3
44

5+
* [toolkit-container] Allow same envars for all runtime configs
6+
57
## v1.13.2
68

79
* Add `nvidia-container-runtime-hook.path` config option to specify NVIDIA Container Runtime Hook path explicitly.

test/container/crio_test.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ testing::crio::hook_created() {
2727
}
2828

2929
testing::crio::hook_cleanup() {
30-
testing::docker_run::toolkit::shell 'crio cleanup'
30+
testing::docker_run::toolkit::shell 'crio cleanup --nvidia-runtime-dir /run/nvidia/toolkit/'
3131

3232
test -z "$(ls -A "${shared_dir}${CRIO_HOOKS_DIR}")"
3333
}

tools/container/container.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/**
2+
# Copyright (c) NVIDIA CORPORATION. All rights reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
**/
16+
17+
package container
18+
19+
import (
20+
"fmt"
21+
"os"
22+
"os/exec"
23+
24+
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/engine"
25+
"github.com/NVIDIA/nvidia-container-toolkit/tools/container/operator"
26+
"github.com/sirupsen/logrus"
27+
"github.com/urfave/cli/v2"
28+
)
29+
30+
const (
31+
restartModeNone = "none"
32+
restartModeSignal = "signal"
33+
restartModeSystemd = "systemd"
34+
)
35+
36+
// Options defines the shared options for the CLIs to configure containers runtimes.
37+
type Options struct {
38+
Config string
39+
Socket string
40+
RuntimeName string
41+
RuntimeDir string
42+
SetAsDefault bool
43+
RestartMode string
44+
HostRootMount string
45+
}
46+
47+
// ParseArgs parses the command line arguments to the CLI
48+
func ParseArgs(c *cli.Context, o *Options) error {
49+
if o.RuntimeDir != "" {
50+
logrus.Debug("Runtime directory already set; ignoring arguments")
51+
return nil
52+
}
53+
54+
args := c.Args()
55+
56+
logrus.Infof("Parsing arguments: %v", args.Slice())
57+
if c.NArg() != 1 {
58+
return fmt.Errorf("incorrect number of arguments")
59+
}
60+
61+
o.RuntimeDir = args.Get(0)
62+
63+
logrus.Infof("Successfully parsed arguments")
64+
65+
return nil
66+
}
67+
68+
// Configure applies the options to the specified config
69+
func (o Options) Configure(cfg engine.Interface) error {
70+
err := o.UpdateConfig(cfg)
71+
if err != nil {
72+
return fmt.Errorf("unable to update config: %v", err)
73+
}
74+
return o.flush(cfg)
75+
}
76+
77+
// Unconfigure removes the options from the specified config
78+
func (o Options) Unconfigure(cfg engine.Interface) error {
79+
err := o.RevertConfig(cfg)
80+
if err != nil {
81+
return fmt.Errorf("unable to update config: %v", err)
82+
}
83+
return o.flush(cfg)
84+
}
85+
86+
// flush flushes the specified config to disk
87+
func (o Options) flush(cfg engine.Interface) error {
88+
logrus.Infof("Flushing config to %v", o.Config)
89+
n, err := cfg.Save(o.Config)
90+
if err != nil {
91+
return fmt.Errorf("unable to flush config: %v", err)
92+
}
93+
if n == 0 {
94+
logrus.Infof("Config file is empty, removed")
95+
}
96+
return nil
97+
}
98+
99+
// UpdateConfig updates the specified config to include the nvidia runtimes
100+
func (o Options) UpdateConfig(cfg engine.Interface) error {
101+
runtimes := operator.GetRuntimes(
102+
operator.WithNvidiaRuntimeName(o.RuntimeName),
103+
operator.WithSetAsDefault(o.SetAsDefault),
104+
operator.WithRoot(o.RuntimeDir),
105+
)
106+
for name, runtime := range runtimes {
107+
err := cfg.AddRuntime(name, runtime.Path, runtime.SetAsDefault)
108+
if err != nil {
109+
return fmt.Errorf("failed to update runtime %q: %v", name, err)
110+
}
111+
}
112+
113+
return nil
114+
}
115+
116+
// RevertConfig reverts the specified config to remove the nvidia runtimes
117+
func (o Options) RevertConfig(cfg engine.Interface) error {
118+
runtimes := operator.GetRuntimes(
119+
operator.WithNvidiaRuntimeName(o.RuntimeName),
120+
operator.WithSetAsDefault(o.SetAsDefault),
121+
operator.WithRoot(o.RuntimeDir),
122+
)
123+
for name := range runtimes {
124+
err := cfg.RemoveRuntime(name)
125+
if err != nil {
126+
return fmt.Errorf("failed to remove runtime %q: %v", name, err)
127+
}
128+
}
129+
130+
return nil
131+
}
132+
133+
// Restart restarts the specified service
134+
func (o Options) Restart(service string, withSignal func(string) error) error {
135+
switch o.RestartMode {
136+
case restartModeNone:
137+
logrus.Warnf("Skipping restart of %v due to --restart-mode=%v", service, o.RestartMode)
138+
return nil
139+
case restartModeSignal:
140+
return withSignal(o.Socket)
141+
case restartModeSystemd:
142+
return o.SystemdRestart(service)
143+
}
144+
145+
return fmt.Errorf("invalid restart mode specified: %v", o.RestartMode)
146+
}
147+
148+
// SystemdRestart restarts the specified service using systemd
149+
func (o Options) SystemdRestart(service string) error {
150+
var args []string
151+
var msg string
152+
if o.HostRootMount != "" {
153+
msg = " on host"
154+
args = append(args, "chroot", o.HostRootMount)
155+
}
156+
args = append(args, "systemctl", "restart", service)
157+
158+
logrus.Infof("Restarting %v%v using systemd: %v", service, msg, args)
159+
160+
cmd := exec.Command(args[0], args[1:]...)
161+
cmd.Stdout = os.Stdout
162+
cmd.Stderr = os.Stderr
163+
err := cmd.Run()
164+
if err != nil {
165+
return fmt.Errorf("error restarting %v using systemd: %v", service, err)
166+
}
167+
168+
return nil
169+
}

tools/container/containerd/config_v1_test.go

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"testing"
2222

2323
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/engine/containerd"
24+
"github.com/NVIDIA/nvidia-container-toolkit/tools/container"
2425
"github.com/pelletier/go-toml"
2526
"github.com/stretchr/testify/require"
2627
)
@@ -31,7 +32,7 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
3132
testCases := []struct {
3233
legacyConfig bool
3334
setAsDefault bool
34-
runtimeClass string
35+
runtimeName string
3536
expectedDefaultRuntimeName interface{}
3637
expectedDefaultRuntimeBinary interface{}
3738
}{
@@ -51,14 +52,14 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
5152
{
5253
legacyConfig: true,
5354
setAsDefault: true,
54-
runtimeClass: "NAME",
55+
runtimeName: "NAME",
5556
expectedDefaultRuntimeName: nil,
5657
expectedDefaultRuntimeBinary: "/test/runtime/dir/nvidia-container-runtime",
5758
},
5859
{
5960
legacyConfig: true,
6061
setAsDefault: true,
61-
runtimeClass: "nvidia-experimental",
62+
runtimeName: "nvidia-experimental",
6263
expectedDefaultRuntimeName: nil,
6364
expectedDefaultRuntimeBinary: "/test/runtime/dir/nvidia-container-runtime.experimental",
6465
},
@@ -77,14 +78,14 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
7778
{
7879
legacyConfig: false,
7980
setAsDefault: true,
80-
runtimeClass: "NAME",
81+
runtimeName: "NAME",
8182
expectedDefaultRuntimeName: "NAME",
8283
expectedDefaultRuntimeBinary: nil,
8384
},
8485
{
8586
legacyConfig: false,
8687
setAsDefault: true,
87-
runtimeClass: "nvidia-experimental",
88+
runtimeName: "nvidia-experimental",
8889
expectedDefaultRuntimeName: "nvidia-experimental",
8990
expectedDefaultRuntimeBinary: nil,
9091
},
@@ -93,11 +94,13 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
9394
for i, tc := range testCases {
9495
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
9596
o := &options{
96-
useLegacyConfig: tc.legacyConfig,
97-
setAsDefault: tc.setAsDefault,
98-
runtimeClass: tc.runtimeClass,
97+
Options: container.Options{
98+
RuntimeName: tc.runtimeName,
99+
RuntimeDir: runtimeDir,
100+
SetAsDefault: tc.setAsDefault,
101+
},
99102
runtimeType: runtimeType,
100-
runtimeDir: runtimeDir,
103+
useLegacyConfig: tc.legacyConfig,
101104
}
102105

103106
config, err := toml.TreeFromMap(map[string]interface{}{})
@@ -109,7 +112,7 @@ func TestUpdateV1ConfigDefaultRuntime(t *testing.T) {
109112
RuntimeType: runtimeType,
110113
}
111114

112-
err = UpdateConfig(v1, o)
115+
err = o.UpdateConfig(v1)
113116
require.NoError(t, err, "%d: %v", i, tc)
114117

115118
defaultRuntimeName := v1.GetPath([]string{"plugins", "cri", "containerd", "default_runtime_name"})
@@ -138,11 +141,11 @@ func TestUpdateV1Config(t *testing.T) {
138141
const runtimeDir = "/test/runtime/dir"
139142

140143
testCases := []struct {
141-
runtimeClass string
144+
runtimeName string
142145
expectedConfig map[string]interface{}
143146
}{
144147
{
145-
runtimeClass: "nvidia",
148+
runtimeName: "nvidia",
146149
expectedConfig: map[string]interface{}{
147150
"version": int64(1),
148151
"plugins": map[string]interface{}{
@@ -200,7 +203,7 @@ func TestUpdateV1Config(t *testing.T) {
200203
},
201204
},
202205
{
203-
runtimeClass: "NAME",
206+
runtimeName: "NAME",
204207
expectedConfig: map[string]interface{}{
205208
"version": int64(1),
206209
"plugins": map[string]interface{}{
@@ -258,7 +261,7 @@ func TestUpdateV1Config(t *testing.T) {
258261
},
259262
},
260263
{
261-
runtimeClass: "nvidia-experimental",
264+
runtimeName: "nvidia-experimental",
262265
expectedConfig: map[string]interface{}{
263266
"version": int64(1),
264267
"plugins": map[string]interface{}{
@@ -320,9 +323,10 @@ func TestUpdateV1Config(t *testing.T) {
320323
for i, tc := range testCases {
321324
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
322325
o := &options{
323-
runtimeClass: tc.runtimeClass,
324-
runtimeType: runtimeType,
325-
runtimeDir: runtimeDir,
326+
Options: container.Options{
327+
RuntimeName: tc.runtimeName,
328+
RuntimeDir: runtimeDir,
329+
},
326330
}
327331

328332
config, err := toml.TreeFromMap(map[string]interface{}{})
@@ -335,7 +339,7 @@ func TestUpdateV1Config(t *testing.T) {
335339
ContainerAnnotations: []string{"cdi.k8s.io/*"},
336340
}
337341

338-
err = UpdateConfig(v1, o)
342+
err = o.UpdateConfig(v1)
339343
require.NoError(t, err)
340344

341345
expected, err := toml.TreeFromMap(tc.expectedConfig)
@@ -350,11 +354,11 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
350354
const runtimeDir = "/test/runtime/dir"
351355

352356
testCases := []struct {
353-
runtimeClass string
357+
runtimeName string
354358
expectedConfig map[string]interface{}
355359
}{
356360
{
357-
runtimeClass: "nvidia",
361+
runtimeName: "nvidia",
358362
expectedConfig: map[string]interface{}{
359363
"version": int64(1),
360364
"plugins": map[string]interface{}{
@@ -426,7 +430,7 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
426430
},
427431
},
428432
{
429-
runtimeClass: "NAME",
433+
runtimeName: "NAME",
430434
expectedConfig: map[string]interface{}{
431435
"version": int64(1),
432436
"plugins": map[string]interface{}{
@@ -498,7 +502,7 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
498502
},
499503
},
500504
{
501-
runtimeClass: "nvidia-experimental",
505+
runtimeName: "nvidia-experimental",
502506
expectedConfig: map[string]interface{}{
503507
"version": int64(1),
504508
"plugins": map[string]interface{}{
@@ -574,9 +578,10 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
574578
for i, tc := range testCases {
575579
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
576580
o := &options{
577-
runtimeClass: tc.runtimeClass,
578-
runtimeType: runtimeType,
579-
runtimeDir: runtimeDir,
581+
Options: container.Options{
582+
RuntimeName: tc.runtimeName,
583+
RuntimeDir: runtimeDir,
584+
},
580585
}
581586

582587
config, err := toml.TreeFromMap(runcConfigMapV1("/runc-binary"))
@@ -589,7 +594,7 @@ func TestUpdateV1ConfigWithRuncPresent(t *testing.T) {
589594
ContainerAnnotations: []string{"cdi.k8s.io/*"},
590595
}
591596

592-
err = UpdateConfig(v1, o)
597+
err = o.UpdateConfig(v1)
593598
require.NoError(t, err)
594599

595600
expected, err := toml.TreeFromMap(tc.expectedConfig)
@@ -653,7 +658,9 @@ func TestRevertV1Config(t *testing.T) {
653658
for i, tc := range testCases {
654659
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
655660
o := &options{
656-
runtimeClass: "nvidia",
661+
Options: container.Options{
662+
RuntimeName: "nvidia",
663+
},
657664
}
658665

659666
config, err := toml.TreeFromMap(tc.config)
@@ -668,7 +675,7 @@ func TestRevertV1Config(t *testing.T) {
668675
RuntimeType: runtimeType,
669676
}
670677

671-
err = RevertConfig(v1, o)
678+
err = o.RevertConfig(v1)
672679
require.NoError(t, err, "%d: %v", i, tc)
673680

674681
configContents, _ := toml.Marshal(config)

0 commit comments

Comments
 (0)