Skip to content

Commit f03e593

Browse files
Adding crio implementation (#50)
Signed-off-by: Vishesh Tanksale <[email protected]>
1 parent 8c9e9dd commit f03e593

File tree

9 files changed

+439
-73
lines changed

9 files changed

+439
-73
lines changed

.golangci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ linters:
1818
linters-settings:
1919
goimports:
2020
local-prefixes: github.com/NVIDIA/k8s-kata-manager
21+
gosec:
22+
excludes:
23+
- G204

api/v1alpha1/config/consts.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@
1616

1717
package config
1818

19+
// Runtime defines container runtime type
20+
type Runtime string
21+
1922
const (
2023
DefaultKataArtifactsDir = "/opt/nvidia-gpu-operator/artifacts/runtimeclasses"
24+
DefaultCrioRuntime = "crun"
25+
// CRIO runtime
26+
CRIO Runtime = "crio"
27+
// Containerd runtime
28+
Containerd Runtime = "containerd"
2129
)
30+
31+
func (r Runtime) String() string {
32+
switch r {
33+
case CRIO:
34+
return "crio"
35+
case Containerd:
36+
return "containerd"
37+
default:
38+
return ""
39+
}
40+
}

cmd/k8s-kata-manager/main.go

Lines changed: 52 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"path/filepath"
2626
"strconv"
2727
"syscall"
28-
"time"
2928

3029
"github.com/pelletier/go-toml"
3130
"github.com/urfave/cli/v2"
@@ -42,12 +41,14 @@ import (
4241
"github.com/NVIDIA/k8s-kata-manager/internal/oras"
4342
"github.com/NVIDIA/k8s-kata-manager/internal/runtime"
4443
containerd "github.com/NVIDIA/k8s-kata-manager/internal/runtime/containerd"
44+
"github.com/NVIDIA/k8s-kata-manager/internal/runtime/crio"
4545
"github.com/NVIDIA/k8s-kata-manager/internal/version"
4646
)
4747

4848
const (
4949
defaultContainerdConfigFilePath = "/etc/containerd/config.toml"
5050
defaultContainerdSocketFilePath = "/run/containerd/containerd.sock"
51+
defaultCrioConfigFilePath = "/etc/crio/crio.conf"
5152

5253
cdiRoot = "/var/run/cdi"
5354
)
@@ -74,6 +75,8 @@ type worker struct {
7475
ContainerdSocket string
7576
LoadKernelModules bool
7677
CDIEnabled bool
78+
Runtime string
79+
CrioConfig string
7780
}
7881

7982
// newWorker returns a new worker struct
@@ -146,6 +149,20 @@ func main() {
146149
Destination: &worker.CDIEnabled,
147150
EnvVars: []string{"CDI_ENABLED"},
148151
},
152+
&cli.StringFlag{
153+
Name: "runtime",
154+
Usage: "Runtime name",
155+
Value: "",
156+
Destination: &worker.Runtime,
157+
EnvVars: []string{"RUNTIME"},
158+
},
159+
&cli.StringFlag{
160+
Name: "crio-config",
161+
Usage: "Path to the CRI-O config file",
162+
Value: defaultCrioConfigFilePath,
163+
Destination: &worker.CrioConfig,
164+
EnvVars: []string{"CRIO_CONFIG"},
165+
},
149166
}
150167

151168
c.Before = func(c *cli.Context) error {
@@ -245,11 +262,9 @@ func (w *worker) Run(c *cli.Context) error {
245262
return fmt.Errorf("failed to generate CDI spec: %w", err)
246263
}
247264
}
248-
249-
options := runtime.Options{Path: w.ContainerdConfig, RuntimeType: "io.containerd.kata.v2", PodAnnotations: []string{"io.katacontainers.*"}}
250-
ctrdConfig, err := containerd.Setup(&options)
265+
runtimeConfig, err := w.getRuntimeConfig()
251266
if err != nil {
252-
klog.Errorf("error creating containerd.config client : %s", err)
267+
klog.Errorf("error creating runtime config client : %s", err)
253268
return err
254269
}
255270

@@ -292,7 +307,7 @@ func (w *worker) Run(c *cli.Context) error {
292307
return fmt.Errorf("error transforming kata configuration file: %w", err)
293308
}
294309

295-
err = ctrdConfig.AddRuntime(
310+
err = runtimeConfig.AddRuntime(
296311
rc.Name,
297312
kataConfigPath,
298313
false,
@@ -302,23 +317,21 @@ func (w *worker) Run(c *cli.Context) error {
302317
}
303318

304319
}
305-
306-
n, err := ctrdConfig.Save(w.ContainerdConfig)
320+
n, err := runtimeConfig.Save()
307321
if err != nil {
308322
return fmt.Errorf("unable to flush config: %w", err)
309323
}
310-
311324
if n == 0 {
312-
klog.Infof("Removed empty config from %v", w.ContainerdConfig)
325+
klog.Infof("Removed empty config")
313326
} else {
314-
klog.Infof("Wrote updated config to %v", w.ContainerdConfig)
327+
klog.Infof("Wrote updated config")
315328
}
316329

317-
klog.Infof("Restarting containerd")
318-
if err := restartContainerd(w.ContainerdSocket); err != nil {
319-
return fmt.Errorf("unable to restart containerd: %w", err)
330+
klog.Infof("Restarting runtime")
331+
if err := runtimeConfig.Restart(); err != nil {
332+
return fmt.Errorf("unable to restart runtime service: %w", err)
320333
}
321-
klog.Info("containerd successfully restarted")
334+
klog.Info("runtime successfully restarted")
322335

323336
if err := waitForSignal(); err != nil {
324337
return fmt.Errorf("unable to wait for signal: %w", err)
@@ -331,22 +344,37 @@ func (w *worker) Run(c *cli.Context) error {
331344
return nil
332345
}
333346

334-
// CleanUp reverts the containerd config to remove the nvidia-container-runtime
347+
func (w *worker) getRuntimeConfig() (runtime.Runtime, error) {
348+
var runtimeConfig runtime.Runtime
349+
var err error
350+
if w.Runtime == api.CRIO.String() {
351+
options := runtime.Options{Path: w.CrioConfig, RuntimeType: "vm", PodAnnotations: []string{"io.katacontainers.*"}}
352+
runtimeConfig, err = crio.Setup(&options)
353+
} else if w.Runtime == api.Containerd.String() {
354+
options := runtime.Options{Path: w.ContainerdConfig, RuntimeType: "io.containerd.kata.v2", PodAnnotations: []string{"io.katacontainers.*"}, Socket: w.ContainerdSocket}
355+
runtimeConfig, err = containerd.Setup(&options)
356+
}
357+
if err != nil {
358+
klog.Errorf("error creating runtime config client : %s", err)
359+
return nil, err
360+
}
361+
return runtimeConfig, nil
362+
}
363+
364+
// CleanUp reverts the runtime config added by kata manager
335365
func (w *worker) CleanUp() error {
336-
ctrdConfig, err := containerd.New(
337-
containerd.WithPath(w.ContainerdConfig),
338-
)
366+
runtimeConfig, err := w.getRuntimeConfig()
339367
if err != nil {
340-
klog.Errorf("error creating containerd.config client : %s", err)
368+
klog.Errorf("error creating runtime config client : %s", err)
341369
return err
342370
}
343371
for _, rc := range w.Config.RuntimeClasses {
344-
err := ctrdConfig.RemoveRuntime(rc.Name)
372+
err := runtimeConfig.RemoveRuntime(rc.Name)
345373
if err != nil {
346374
return fmt.Errorf("unable to revert config for runtime class '%v': %w", rc, err)
347375
}
348376
}
349-
n, err := ctrdConfig.Save(w.ContainerdConfig)
377+
n, err := runtimeConfig.Save()
350378
if err != nil {
351379
return fmt.Errorf("unable to flush config: %w", err)
352380
}
@@ -356,8 +384,8 @@ func (w *worker) CleanUp() error {
356384
} else {
357385
klog.Infof("Wrote updated config to %v", w.ContainerdConfig)
358386
}
359-
if err := restartContainerd(w.ContainerdSocket); err != nil {
360-
return fmt.Errorf("unable to restart containerd: %w", err)
387+
if err := runtimeConfig.Restart(); err != nil {
388+
return fmt.Errorf("unable to restart runtime service: %w", err)
361389
}
362390
return nil
363391
}
@@ -385,48 +413,6 @@ func initialize() error {
385413
return nil
386414
}
387415

388-
func restartContainerd(containerdSocket string) error {
389-
390-
// Create a channel to receive signals
391-
sigs := make(chan os.Signal, 1)
392-
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGHUP)
393-
394-
// Set up a timer to ignore the signal for 5 seconds
395-
ignoreTimer := time.NewTimer(5 * time.Second)
396-
397-
// Create a channel to signal when the function has finished executing
398-
done := make(chan error)
399-
400-
// Start the function in a goroutine
401-
go func() {
402-
// Execute your function here
403-
err := containerd.RestartContainerd(containerdSocket)
404-
if err != nil {
405-
klog.Errorf("error restarting containerd: %v", err)
406-
done <- err
407-
}
408-
// Since we are restarintg Containerd we need to
409-
// Ignore the SIGTERM signal for 5 seconds
410-
<-ignoreTimer.C
411-
// Signal that the function has finished executing
412-
done <- nil
413-
}()
414-
415-
// Wait for the function to finish executing or for the signal to be received
416-
select {
417-
case err := <-done:
418-
if err != nil {
419-
return err
420-
}
421-
case s := <-sigs:
422-
fmt.Printf("Received signal %v", s)
423-
// Reset the timer to ignore the signal for another 5 seconds
424-
ignoreTimer.Reset(5 * time.Second)
425-
}
426-
427-
return nil
428-
}
429-
430416
func transformKataConfig(path string) error {
431417
config, err := toml.LoadFile(path)
432418
if err != nil {

internal/cdi/nvpci-interface_mock.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/runtime/containerd/containerd.go

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,12 @@ package containerd
1919
import (
2020
"fmt"
2121
"os"
22+
"os/signal"
23+
"syscall"
24+
"time"
2225

2326
"github.com/pelletier/go-toml"
27+
"k8s.io/klog/v2"
2428

2529
"github.com/NVIDIA/k8s-kata-manager/internal/runtime"
2630
)
@@ -31,13 +35,16 @@ type Config struct {
3135
RuntimeType string
3236
UseDefaultRuntimeName bool
3337
PodAnnotations []string
38+
Path string
39+
Socket string
3440
}
3541

3642
func Setup(o *runtime.Options) (runtime.Runtime, error) {
3743
ctrdConfig, err := New(
3844
WithPath(o.Path),
3945
WithPodAnnotations(o.PodAnnotations...),
4046
WithRuntimeType(o.RuntimeType),
47+
WithSocket(o.Socket),
4148
)
4249
return ctrdConfig, err
4350
}
@@ -134,29 +141,29 @@ func (c *Config) RemoveRuntime(name string) error {
134141
}
135142

136143
// Save writes the config to the specified path
137-
func (c *Config) Save(path string) (int64, error) {
144+
func (c *Config) Save() (int64, error) {
138145
config := c.Tree
139146
output, err := config.ToTomlString()
140147
if err != nil {
141148
return 0, fmt.Errorf("unable to convert to TOML: %w", err)
142149
}
143150

144-
if path == "" {
151+
if c.Path == "" {
145152
os.Stdout.WriteString(fmt.Sprintf("%s\n", output))
146153
return int64(len(output)), nil
147154
}
148155

149156
if len(output) == 0 {
150-
err := os.Remove(path)
157+
err := os.Remove(c.Path)
151158
if err != nil {
152159
return 0, fmt.Errorf("unable to remove empty file: %w", err)
153160
}
154161
return 0, nil
155162
}
156163

157-
f, err := os.Create(path)
164+
f, err := os.Create(c.Path)
158165
if err != nil {
159-
return 0, fmt.Errorf("unable to open '%s' for writing: %w", path, err)
166+
return 0, fmt.Errorf("unable to open '%s' for writing: %w", c.Path, err)
160167
}
161168
defer f.Close()
162169

@@ -167,3 +174,45 @@ func (c *Config) Save(path string) (int64, error) {
167174

168175
return int64(n), err
169176
}
177+
178+
func (c *Config) Restart() error {
179+
180+
// Create a channel to receive signals
181+
sigs := make(chan os.Signal, 1)
182+
signal.Notify(sigs, syscall.SIGTERM, syscall.SIGHUP)
183+
184+
// Set up a timer to ignore the signal for 5 seconds
185+
ignoreTimer := time.NewTimer(5 * time.Second)
186+
187+
// Create a channel to signal when the function has finished executing
188+
done := make(chan error)
189+
190+
// Start the function in a goroutine
191+
go func() {
192+
// Execute your function here
193+
err := RestartContainerd(c.Socket)
194+
if err != nil {
195+
klog.Errorf("error restarting containerd: %v", err)
196+
done <- err
197+
}
198+
// Since we are restarting containerd we need to
199+
// Ignore the SIGTERM signal for 5 seconds
200+
<-ignoreTimer.C
201+
// Signal that the function has finished executing
202+
done <- nil
203+
}()
204+
205+
// Wait for the function to finish executing or for the signal to be received
206+
select {
207+
case err := <-done:
208+
if err != nil {
209+
return err
210+
}
211+
case s := <-sigs:
212+
fmt.Printf("Received signal %v", s)
213+
// Reset the timer to ignore the signal for another 5 seconds
214+
ignoreTimer.Reset(5 * time.Second)
215+
}
216+
217+
return nil
218+
}

internal/runtime/containerd/option.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type builder struct {
3333
runtimeType string
3434
useLegacyConfig bool
3535
podAnnotations []string
36+
socket string
3637
}
3738

3839
// Option defines a function that can be used to configure the config builder
@@ -66,6 +67,13 @@ func WithPodAnnotations(podAnnotations ...string) Option {
6667
}
6768
}
6869

70+
// WithSocket sets the socket for the config builder
71+
func WithSocket(socket string) Option {
72+
return func(b *builder) {
73+
b.socket = socket
74+
}
75+
}
76+
6977
func (b *builder) build() (*Config, error) {
7078
if b.path == "" {
7179
return &Config{}, fmt.Errorf("config path is empty")
@@ -82,6 +90,8 @@ func (b *builder) build() (*Config, error) {
8290
config.RuntimeType = b.runtimeType
8391
config.UseDefaultRuntimeName = !b.useLegacyConfig
8492
config.PodAnnotations = b.podAnnotations
93+
config.Path = b.path
94+
config.Socket = b.socket
8595

8696
return config, nil
8797
}

0 commit comments

Comments
 (0)