1 package specconv
2
3 import (
4 "os"
5 "strings"
6 "testing"
7
8 dbus "github.com/godbus/dbus/v5"
9 "github.com/opencontainers/runc/libcontainer/configs"
10 "github.com/opencontainers/runc/libcontainer/configs/validate"
11 "github.com/opencontainers/runc/libcontainer/devices"
12 "github.com/opencontainers/runtime-spec/specs-go"
13 "golang.org/x/sys/unix"
14 )
15
16 func TestCreateCommandHookTimeout(t *testing.T) {
17 timeout := 3600
18 hook := specs.Hook{
19 Path: "/some/hook/path",
20 Args: []string{"--some", "thing"},
21 Env: []string{"SOME=value"},
22 Timeout: &timeout,
23 }
24 command := createCommandHook(hook)
25 timeoutStr := command.Timeout.String()
26 if timeoutStr != "1h0m0s" {
27 t.Errorf("Expected the Timeout to be 1h0m0s, got: %s", timeoutStr)
28 }
29 }
30
31 func TestCreateHooks(t *testing.T) {
32 rspec := &specs.Spec{
33 Hooks: &specs.Hooks{
34 Prestart: []specs.Hook{
35 {
36 Path: "/some/hook/path",
37 },
38 {
39 Path: "/some/hook2/path",
40 Args: []string{"--some", "thing"},
41 },
42 },
43 CreateRuntime: []specs.Hook{
44 {
45 Path: "/some/hook/path",
46 },
47 {
48 Path: "/some/hook2/path",
49 Args: []string{"--some", "thing"},
50 },
51 },
52 CreateContainer: []specs.Hook{
53 {
54 Path: "/some/hook/path",
55 },
56 {
57 Path: "/some/hook2/path",
58 Args: []string{"--some", "thing"},
59 },
60 },
61 StartContainer: []specs.Hook{
62 {
63 Path: "/some/hook/path",
64 },
65 {
66 Path: "/some/hook2/path",
67 Args: []string{"--some", "thing"},
68 },
69 },
70 Poststart: []specs.Hook{
71 {
72 Path: "/some/hook/path",
73 Args: []string{"--some", "thing"},
74 Env: []string{"SOME=value"},
75 },
76 {
77 Path: "/some/hook2/path",
78 },
79 {
80 Path: "/some/hook3/path",
81 },
82 },
83 Poststop: []specs.Hook{
84 {
85 Path: "/some/hook/path",
86 Args: []string{"--some", "thing"},
87 Env: []string{"SOME=value"},
88 },
89 {
90 Path: "/some/hook2/path",
91 },
92 {
93 Path: "/some/hook3/path",
94 },
95 {
96 Path: "/some/hook4/path",
97 Args: []string{"--some", "thing"},
98 },
99 },
100 },
101 }
102 conf := &configs.Config{}
103 createHooks(rspec, conf)
104
105 prestart := conf.Hooks[configs.Prestart]
106
107 if len(prestart) != 2 {
108 t.Error("Expected 2 Prestart hooks")
109 }
110
111 createRuntime := conf.Hooks[configs.CreateRuntime]
112
113 if len(createRuntime) != 2 {
114 t.Error("Expected 2 createRuntime hooks")
115 }
116
117 createContainer := conf.Hooks[configs.CreateContainer]
118
119 if len(createContainer) != 2 {
120 t.Error("Expected 2 createContainer hooks")
121 }
122
123 startContainer := conf.Hooks[configs.StartContainer]
124
125 if len(startContainer) != 2 {
126 t.Error("Expected 2 startContainer hooks")
127 }
128
129 poststart := conf.Hooks[configs.Poststart]
130
131 if len(poststart) != 3 {
132 t.Error("Expected 3 Poststart hooks")
133 }
134
135 poststop := conf.Hooks[configs.Poststop]
136
137 if len(poststop) != 4 {
138 t.Error("Expected 4 Poststop hooks")
139 }
140 }
141
142 func TestSetupSeccompNil(t *testing.T) {
143 seccomp, err := SetupSeccomp(nil)
144 if err != nil {
145 t.Error("Expected error to be nil")
146 }
147
148 if seccomp != nil {
149 t.Error("Expected seccomp to be nil")
150 }
151 }
152
153 func TestSetupSeccompEmpty(t *testing.T) {
154 conf := &specs.LinuxSeccomp{}
155 seccomp, err := SetupSeccomp(conf)
156 if err != nil {
157 t.Error("Expected error to be nil")
158 }
159
160 if seccomp != nil {
161 t.Error("Expected seccomp to be nil")
162 }
163 }
164
165
166 func TestSetupSeccompWrongAction(t *testing.T) {
167 conf := &specs.LinuxSeccomp{
168 DefaultAction: "SCMP_ACT_NON_EXIXTENT_ACTION",
169 }
170 _, err := SetupSeccomp(conf)
171 if err == nil {
172 t.Error("Expected error")
173 }
174 }
175
176
177 func TestSetupSeccompWrongArchitecture(t *testing.T) {
178 conf := &specs.LinuxSeccomp{
179 DefaultAction: "SCMP_ACT_ALLOW",
180 Architectures: []specs.Arch{"SCMP_ARCH_NON_EXISTENT_ARCH"},
181 }
182 _, err := SetupSeccomp(conf)
183 if err == nil {
184 t.Error("Expected error")
185 }
186 }
187
188 func TestSetupSeccomp(t *testing.T) {
189 errnoRet := uint(55)
190 conf := &specs.LinuxSeccomp{
191 DefaultAction: "SCMP_ACT_ERRNO",
192 Architectures: []specs.Arch{specs.ArchX86_64, specs.ArchARM},
193 ListenerPath: "/var/run/mysocket",
194 ListenerMetadata: "mymetadatastring",
195 Syscalls: []specs.LinuxSyscall{
196 {
197 Names: []string{"clone"},
198 Action: "SCMP_ACT_ALLOW",
199 Args: []specs.LinuxSeccompArg{
200 {
201 Index: 0,
202 Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET | unix.CLONE_NEWCGROUP,
203 ValueTwo: 0,
204 Op: "SCMP_CMP_MASKED_EQ",
205 },
206 },
207 },
208 {
209 Names: []string{"semctl"},
210 Action: "SCMP_ACT_KILL",
211 },
212 {
213 Names: []string{"semget"},
214 Action: "SCMP_ACT_ERRNO",
215 },
216 {
217 Names: []string{"send"},
218 Action: "SCMP_ACT_ERRNO",
219 ErrnoRet: &errnoRet,
220 },
221 {
222 Names: []string{"lchown"},
223 Action: "SCMP_ACT_TRAP",
224 },
225 {
226 Names: []string{"lremovexattr"},
227 Action: "SCMP_ACT_TRACE",
228 },
229 {
230 Names: []string{"mbind"},
231 Action: "SCMP_ACT_LOG",
232 },
233 {
234 Names: []string{"mknod"},
235 Action: "SCMP_ACT_NOTIFY",
236 },
237 {
238 Names: []string{"rmdir"},
239 Action: "SCMP_ACT_KILL_THREAD",
240 },
241 {
242 Names: []string{"mkdir"},
243 Action: "SCMP_ACT_KILL_PROCESS",
244 },
245 },
246 }
247 seccomp, err := SetupSeccomp(conf)
248 if err != nil {
249 t.Errorf("Couldn't create Seccomp config: %v", err)
250 }
251
252 if seccomp.DefaultAction != configs.Errno {
253 t.Error("Wrong conversion for DefaultAction")
254 }
255
256 if len(seccomp.Architectures) != 2 {
257 t.Error("Wrong number of architectures")
258 }
259
260 if seccomp.Architectures[0] != "amd64" || seccomp.Architectures[1] != "arm" {
261 t.Error("Expected architectures are not found")
262 }
263
264 if seccomp.ListenerPath != "/var/run/mysocket" {
265 t.Error("Expected ListenerPath is wrong")
266 }
267
268 if seccomp.ListenerMetadata != "mymetadatastring" {
269 t.Error("Expected ListenerMetadata is wrong")
270 }
271
272 calls := seccomp.Syscalls
273
274 if len(calls) != len(conf.Syscalls) {
275 t.Error("Mismatched number of syscalls")
276 }
277
278 for _, call := range calls {
279 switch call.Name {
280 case "clone":
281 if call.Action != configs.Allow {
282 t.Error("Wrong conversion for the clone syscall action")
283 }
284 expectedCloneSyscallArgs := configs.Arg{
285 Index: 0,
286 Op: configs.MaskEqualTo,
287 Value: unix.CLONE_NEWNS | unix.CLONE_NEWUTS | unix.CLONE_NEWIPC | unix.CLONE_NEWUSER | unix.CLONE_NEWPID | unix.CLONE_NEWNET | unix.CLONE_NEWCGROUP,
288 ValueTwo: 0,
289 }
290 if expectedCloneSyscallArgs != *call.Args[0] {
291 t.Errorf("Wrong arguments conversion for the clone syscall under test")
292 }
293 case "semctl":
294 if call.Action != configs.Kill {
295 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
296 }
297 case "semget":
298 if call.Action != configs.Errno {
299 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
300 }
301 if call.ErrnoRet != nil {
302 t.Errorf("Wrong error ret for the %s syscall", call.Name)
303 }
304 case "send":
305 if call.Action != configs.Errno {
306 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
307 }
308 if *call.ErrnoRet != errnoRet {
309 t.Errorf("Wrong error ret for the %s syscall", call.Name)
310 }
311 case "lchown":
312 if call.Action != configs.Trap {
313 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
314 }
315 case "lremovexattr":
316 if call.Action != configs.Trace {
317 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
318 }
319 case "mbind":
320 if call.Action != configs.Log {
321 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
322 }
323 case "mknod":
324 if call.Action != configs.Notify {
325 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
326 }
327 case "rmdir":
328 if call.Action != configs.KillThread {
329 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
330 }
331 case "mkdir":
332 if call.Action != configs.KillProcess {
333 t.Errorf("Wrong conversion for the %s syscall action", call.Name)
334 }
335 default:
336 t.Errorf("Unexpected syscall %s found", call.Name)
337 }
338 }
339 }
340
341 func TestLinuxCgroupWithMemoryResource(t *testing.T) {
342 cgroupsPath := "/user/cgroups/path/id"
343
344 spec := &specs.Spec{}
345 devices := []specs.LinuxDeviceCgroup{
346 {
347 Allow: false,
348 Access: "rwm",
349 },
350 }
351
352 limit := int64(100)
353 reservation := int64(50)
354 swap := int64(20)
355 kernel := int64(40)
356 kernelTCP := int64(45)
357 swappiness := uint64(1)
358 swappinessPtr := &swappiness
359 disableOOMKiller := true
360 resources := &specs.LinuxResources{
361 Devices: devices,
362 Memory: &specs.LinuxMemory{
363 Limit: &limit,
364 Reservation: &reservation,
365 Swap: &swap,
366 Kernel: &kernel,
367 KernelTCP: &kernelTCP,
368 Swappiness: swappinessPtr,
369 DisableOOMKiller: &disableOOMKiller,
370 },
371 }
372 spec.Linux = &specs.Linux{
373 CgroupsPath: cgroupsPath,
374 Resources: resources,
375 }
376
377 opts := &CreateOpts{
378 CgroupName: "ContainerID",
379 UseSystemdCgroup: false,
380 Spec: spec,
381 }
382
383 cgroup, err := CreateCgroupConfig(opts, nil)
384 if err != nil {
385 t.Errorf("Couldn't create Cgroup config: %v", err)
386 }
387
388 if cgroup.Path != cgroupsPath {
389 t.Errorf("Wrong cgroupsPath, expected '%s' got '%s'", cgroupsPath, cgroup.Path)
390 }
391 if cgroup.Resources.Memory != limit {
392 t.Errorf("Expected to have %d as memory limit, got %d", limit, cgroup.Resources.Memory)
393 }
394 if cgroup.Resources.MemoryReservation != reservation {
395 t.Errorf("Expected to have %d as memory reservation, got %d", reservation, cgroup.Resources.MemoryReservation)
396 }
397 if cgroup.Resources.MemorySwap != swap {
398 t.Errorf("Expected to have %d as swap, got %d", swap, cgroup.Resources.MemorySwap)
399 }
400 if cgroup.Resources.MemorySwappiness != swappinessPtr {
401 t.Errorf("Expected to have %d as memory swappiness, got %d", swappinessPtr, cgroup.Resources.MemorySwappiness)
402 }
403 if cgroup.Resources.OomKillDisable != disableOOMKiller {
404 t.Errorf("The OOMKiller should be enabled")
405 }
406 }
407
408 func TestLinuxCgroupSystemd(t *testing.T) {
409 cgroupsPath := "parent:scopeprefix:name"
410
411 spec := &specs.Spec{}
412 spec.Linux = &specs.Linux{
413 CgroupsPath: cgroupsPath,
414 }
415
416 opts := &CreateOpts{
417 UseSystemdCgroup: true,
418 Spec: spec,
419 }
420
421 cgroup, err := CreateCgroupConfig(opts, nil)
422 if err != nil {
423 t.Errorf("Couldn't create Cgroup config: %v", err)
424 }
425
426 expectedParent := "parent"
427 if cgroup.Parent != expectedParent {
428 t.Errorf("Expected to have %s as Parent instead of %s", expectedParent, cgroup.Parent)
429 }
430
431 expectedScopePrefix := "scopeprefix"
432 if cgroup.ScopePrefix != expectedScopePrefix {
433 t.Errorf("Expected to have %s as ScopePrefix instead of %s", expectedScopePrefix, cgroup.ScopePrefix)
434 }
435
436 expectedName := "name"
437 if cgroup.Name != expectedName {
438 t.Errorf("Expected to have %s as Name instead of %s", expectedName, cgroup.Name)
439 }
440 }
441
442 func TestLinuxCgroupSystemdWithEmptyPath(t *testing.T) {
443 cgroupsPath := ""
444
445 spec := &specs.Spec{}
446 spec.Linux = &specs.Linux{
447 CgroupsPath: cgroupsPath,
448 }
449
450 opts := &CreateOpts{
451 CgroupName: "ContainerID",
452 UseSystemdCgroup: true,
453 Spec: spec,
454 }
455
456 cgroup, err := CreateCgroupConfig(opts, nil)
457 if err != nil {
458 t.Errorf("Couldn't create Cgroup config: %v", err)
459 }
460
461 expectedParent := ""
462 if cgroup.Parent != expectedParent {
463 t.Errorf("Expected to have %s as Parent instead of %s", expectedParent, cgroup.Parent)
464 }
465
466 expectedScopePrefix := "runc"
467 if cgroup.ScopePrefix != expectedScopePrefix {
468 t.Errorf("Expected to have %s as ScopePrefix instead of %s", expectedScopePrefix, cgroup.ScopePrefix)
469 }
470
471 if cgroup.Name != opts.CgroupName {
472 t.Errorf("Expected to have %s as Name instead of %s", opts.CgroupName, cgroup.Name)
473 }
474 }
475
476 func TestLinuxCgroupSystemdWithInvalidPath(t *testing.T) {
477 cgroupsPath := "/user/cgroups/path/id"
478
479 spec := &specs.Spec{}
480 spec.Linux = &specs.Linux{
481 CgroupsPath: cgroupsPath,
482 }
483
484 opts := &CreateOpts{
485 CgroupName: "ContainerID",
486 UseSystemdCgroup: true,
487 Spec: spec,
488 }
489
490 _, err := CreateCgroupConfig(opts, nil)
491 if err == nil {
492 t.Error("Expected to produce an error if not using the correct format for cgroup paths belonging to systemd")
493 }
494 }
495
496 func TestLinuxCgroupsPathSpecified(t *testing.T) {
497 cgroupsPath := "/user/cgroups/path/id"
498
499 spec := &specs.Spec{}
500 spec.Linux = &specs.Linux{
501 CgroupsPath: cgroupsPath,
502 }
503
504 opts := &CreateOpts{
505 CgroupName: "ContainerID",
506 UseSystemdCgroup: false,
507 Spec: spec,
508 }
509
510 cgroup, err := CreateCgroupConfig(opts, nil)
511 if err != nil {
512 t.Errorf("Couldn't create Cgroup config: %v", err)
513 }
514
515 if cgroup.Path != cgroupsPath {
516 t.Errorf("Wrong cgroupsPath, expected '%s' got '%s'", cgroupsPath, cgroup.Path)
517 }
518 }
519
520 func TestLinuxCgroupsPathNotSpecified(t *testing.T) {
521 spec := &specs.Spec{}
522 opts := &CreateOpts{
523 CgroupName: "ContainerID",
524 UseSystemdCgroup: false,
525 Spec: spec,
526 }
527
528 cgroup, err := CreateCgroupConfig(opts, nil)
529 if err != nil {
530 t.Errorf("Couldn't create Cgroup config: %v", err)
531 }
532
533 if cgroup.Path != "" {
534 t.Errorf("Wrong cgroupsPath, expected it to be empty string, got '%s'", cgroup.Path)
535 }
536 }
537
538 func TestSpecconvExampleValidate(t *testing.T) {
539 spec := Example()
540 spec.Root.Path = "/"
541
542 opts := &CreateOpts{
543 CgroupName: "ContainerID",
544 UseSystemdCgroup: false,
545 Spec: spec,
546 }
547
548 config, err := CreateLibcontainerConfig(opts)
549 if err != nil {
550 t.Errorf("Couldn't create libcontainer config: %v", err)
551 }
552
553 if config.NoNewPrivileges != spec.Process.NoNewPrivileges {
554 t.Errorf("specconv NoNewPrivileges mismatch. Expected %v got %v",
555 spec.Process.NoNewPrivileges, config.NoNewPrivileges)
556 }
557
558 validator := validate.New()
559 if err := validator.Validate(config); err != nil {
560 t.Errorf("Expected specconv to produce valid container config: %v", err)
561 }
562 }
563
564 func TestSpecconvNoLinuxSection(t *testing.T) {
565 spec := Example()
566 spec.Root.Path = "/"
567 spec.Linux = nil
568 spec.Hostname = ""
569
570 opts := &CreateOpts{
571 CgroupName: "ContainerID",
572 Spec: spec,
573 }
574
575 config, err := CreateLibcontainerConfig(opts)
576 if err != nil {
577 t.Errorf("Couldn't create libcontainer config: %v", err)
578 }
579
580 validator := validate.New()
581 if err := validator.Validate(config); err != nil {
582 t.Errorf("Expected specconv to produce valid container config: %v", err)
583 }
584 }
585
586 func TestDupNamespaces(t *testing.T) {
587 spec := &specs.Spec{
588 Root: &specs.Root{
589 Path: "rootfs",
590 },
591 Linux: &specs.Linux{
592 Namespaces: []specs.LinuxNamespace{
593 {
594 Type: "pid",
595 },
596 {
597 Type: "pid",
598 Path: "/proc/1/ns/pid",
599 },
600 },
601 },
602 }
603
604 _, err := CreateLibcontainerConfig(&CreateOpts{
605 Spec: spec,
606 })
607
608 if !strings.Contains(err.Error(), "malformed spec file: duplicated ns") {
609 t.Errorf("Duplicated namespaces should be forbidden")
610 }
611 }
612
613 func TestUserNamespaceMappingAndPath(t *testing.T) {
614 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
615 t.Skip("Test requires userns.")
616 }
617
618 spec := &specs.Spec{
619 Root: &specs.Root{
620 Path: "rootfs",
621 },
622 Linux: &specs.Linux{
623 UIDMappings: []specs.LinuxIDMapping{
624 {ContainerID: 0, HostID: 1000, Size: 1000},
625 },
626 GIDMappings: []specs.LinuxIDMapping{
627 {ContainerID: 0, HostID: 2000, Size: 1000},
628 },
629 Namespaces: []specs.LinuxNamespace{
630 {
631 Type: "user",
632 Path: "/proc/1/ns/user",
633 },
634 },
635 },
636 }
637
638 _, err := CreateLibcontainerConfig(&CreateOpts{
639 Spec: spec,
640 })
641
642 if !strings.Contains(err.Error(), "both namespace path and non-matching mapping specified") {
643 t.Errorf("user namespace with path and non-matching mapping should be forbidden, got error %v", err)
644 }
645 }
646
647 func TestNonZeroEUIDCompatibleSpecconvValidate(t *testing.T) {
648 if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
649 t.Skip("Test requires userns.")
650 }
651
652 spec := Example()
653 spec.Root.Path = "/"
654 ToRootless(spec)
655
656 opts := &CreateOpts{
657 CgroupName: "ContainerID",
658 UseSystemdCgroup: false,
659 Spec: spec,
660 RootlessEUID: true,
661 RootlessCgroups: true,
662 }
663
664 config, err := CreateLibcontainerConfig(opts)
665 if err != nil {
666 t.Errorf("Couldn't create libcontainer config: %v", err)
667 }
668
669 validator := validate.New()
670 if err := validator.Validate(config); err != nil {
671 t.Errorf("Expected specconv to produce valid rootless container config: %v", err)
672 }
673 }
674
675 func TestInitSystemdProps(t *testing.T) {
676 type inT struct {
677 name, value string
678 }
679 type expT struct {
680 isErr bool
681 name string
682 value interface{}
683 }
684
685 testCases := []struct {
686 desc string
687 in inT
688 exp expT
689 }{
690 {
691 in: inT{"org.systemd.property.TimeoutStopUSec", "uint64 123456789"},
692 exp: expT{false, "TimeoutStopUSec", uint64(123456789)},
693 },
694 {
695 desc: "convert USec to Sec (default numeric type)",
696 in: inT{"org.systemd.property.TimeoutStopSec", "456"},
697 exp: expT{false, "TimeoutStopUSec", uint64(456000000)},
698 },
699 {
700 desc: "convert USec to Sec (byte)",
701 in: inT{"org.systemd.property.TimeoutStopSec", "byte 234"},
702 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
703 },
704 {
705 desc: "convert USec to Sec (int16)",
706 in: inT{"org.systemd.property.TimeoutStopSec", "int16 234"},
707 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
708 },
709 {
710 desc: "convert USec to Sec (uint16)",
711 in: inT{"org.systemd.property.TimeoutStopSec", "uint16 234"},
712 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
713 },
714 {
715 desc: "convert USec to Sec (int32)",
716 in: inT{"org.systemd.property.TimeoutStopSec", "int32 234"},
717 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
718 },
719 {
720 desc: "convert USec to Sec (uint32)",
721 in: inT{"org.systemd.property.TimeoutStopSec", "uint32 234"},
722 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
723 },
724 {
725 desc: "convert USec to Sec (int64)",
726 in: inT{"org.systemd.property.TimeoutStopSec", "int64 234"},
727 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
728 },
729 {
730 desc: "convert USec to Sec (uint64)",
731 in: inT{"org.systemd.property.TimeoutStopSec", "uint64 234"},
732 exp: expT{false, "TimeoutStopUSec", uint64(234000000)},
733 },
734 {
735 desc: "convert USec to Sec (float)",
736 in: inT{"org.systemd.property.TimeoutStopSec", "234.789"},
737 exp: expT{false, "TimeoutStopUSec", uint64(234789000)},
738 },
739 {
740 desc: "convert USec to Sec (bool -- invalid value)",
741 in: inT{"org.systemd.property.TimeoutStopSec", "false"},
742 exp: expT{true, "", ""},
743 },
744 {
745 desc: "convert USec to Sec (string -- invalid value)",
746 in: inT{"org.systemd.property.TimeoutStopSec", "'covfefe'"},
747 exp: expT{true, "", ""},
748 },
749 {
750 desc: "convert USec to Sec (bad variable name, no conversion)",
751 in: inT{"org.systemd.property.FOOSec", "123"},
752 exp: expT{false, "FOOSec", 123},
753 },
754 {
755 in: inT{"org.systemd.property.CollectMode", "'inactive-or-failed'"},
756 exp: expT{false, "CollectMode", "inactive-or-failed"},
757 },
758 {
759 desc: "unrelated property",
760 in: inT{"some.other.annotation", "0"},
761 exp: expT{false, "", ""},
762 },
763 {
764 desc: "too short property name",
765 in: inT{"org.systemd.property.Xo", "1"},
766 exp: expT{true, "", ""},
767 },
768 {
769 desc: "invalid character in property name",
770 in: inT{"org.systemd.property.Number1", "1"},
771 exp: expT{true, "", ""},
772 },
773 {
774 desc: "invalid property value",
775 in: inT{"org.systemd.property.ValidName", "invalid-value"},
776 exp: expT{true, "", ""},
777 },
778 }
779
780 spec := &specs.Spec{}
781
782 for _, tc := range testCases {
783 tc := tc
784 spec.Annotations = map[string]string{tc.in.name: tc.in.value}
785
786 outMap, err := initSystemdProps(spec)
787
788
789 if tc.exp.isErr != (err != nil) {
790 t.Errorf("input %+v, expecting error: %v, got %v", tc.in, tc.exp.isErr, err)
791 }
792 expLen := 1
793 if tc.exp.name == "" {
794 expLen = 0
795 }
796 if len(outMap) != expLen {
797 t.Fatalf("input %+v, expected %d, got %d entries: %v", tc.in, expLen, len(outMap), outMap)
798 }
799 if expLen == 0 {
800 continue
801 }
802
803 out := outMap[0]
804 if tc.exp.name != out.Name {
805 t.Errorf("input %+v, expecting name: %q, got %q", tc.in, tc.exp.name, out.Name)
806 }
807 expValue := dbus.MakeVariant(tc.exp.value).String()
808 if expValue != out.Value.String() {
809 t.Errorf("input %+v, expecting value: %s, got %s", tc.in, expValue, out.Value)
810 }
811 }
812 }
813
814 func TestIsValidName(t *testing.T) {
815 testCases := []struct {
816 in string
817 valid bool
818 }{
819 {"", false},
820 {"xx", false},
821 {"xxx", true},
822 {"someValidName", true},
823 {"A name", false},
824 {"3335", false},
825 {"Name1", false},
826 {"Кир", false},
827 {"მადლობა", false},
828 {"合い言葉", false},
829 }
830
831 for _, tc := range testCases {
832 err := checkPropertyName(tc.in)
833 if (err == nil) != tc.valid {
834 t.Errorf("case %q: expected valid: %v, got error: %v", tc.in, tc.valid, err)
835 }
836 }
837 }
838
839 func BenchmarkIsValidName(b *testing.B) {
840 for i := 0; i < b.N; i++ {
841 for _, s := range []string{"", "xx", "xxx", "someValidName", "A name", "Кир", "მადლობა", "合い言葉"} {
842 _ = checkPropertyName(s)
843 }
844 }
845 }
846
847 func TestNullProcess(t *testing.T) {
848 spec := Example()
849 spec.Process = nil
850
851 _, err := CreateLibcontainerConfig(&CreateOpts{
852 Spec: spec,
853 })
854 if err != nil {
855 t.Errorf("Null process should be forbidden")
856 }
857 }
858
859 func TestCreateDevices(t *testing.T) {
860 spec := Example()
861
862
863
864 ttyUid := uint32(1000)
865 ttyGid := uint32(1000)
866 fm := os.FileMode(0o666)
867
868 spec.Linux = &specs.Linux{
869 Devices: []specs.LinuxDevice{
870 {
871
872 Path: "/dev/tty",
873 Type: "c",
874 Major: 5,
875 Minor: 0,
876 FileMode: &fm,
877 UID: &ttyUid,
878 GID: &ttyGid,
879 },
880 {
881
882 Path: "/dev/ram0",
883 Type: "b",
884 Major: 1,
885 Minor: 0,
886 },
887 },
888 }
889
890 conf := &configs.Config{}
891
892 defaultDevs, err := createDevices(spec, conf)
893 if err != nil {
894 t.Errorf("failed to create devices: %v", err)
895 }
896
897
898 found := false
899 for _, d := range defaultDevs {
900 if d.Path == "/dev/tty" {
901 if found {
902 t.Errorf("createDevices failed: returned a duplicated device entry: %v", defaultDevs)
903 }
904 found = true
905 }
906 }
907
908
909 for _, allowedDev := range AllowedDevices {
910 if allowedDev.Path == "" {
911 continue
912 }
913
914 found := false
915 for _, configDev := range conf.Devices {
916 if configDev.Path == allowedDev.Path {
917 found = true
918 }
919 }
920 if !found {
921 configDevPaths := []string{}
922 for _, configDev := range conf.Devices {
923 configDevPaths = append(configDevPaths, configDev.Path)
924 }
925 t.Errorf("allowedDevice %s was not found in the config's devices: %v", allowedDev.Path, configDevPaths)
926 }
927 }
928
929
930 for _, configDev := range conf.Devices {
931 if configDev.Path == "/dev/tty" {
932 wantDev := &devices.Device{
933 Path: "/dev/tty",
934 FileMode: 0o666,
935 Uid: 1000,
936 Gid: 1000,
937 Rule: devices.Rule{
938 Type: devices.CharDevice,
939 Major: 5,
940 Minor: 0,
941 },
942 }
943
944 if *configDev != *wantDev {
945 t.Errorf("redundant dev was not deduplicated correctly: want %v, got %v", wantDev, configDev)
946 }
947 }
948 }
949
950
951 found = false
952 for _, configDev := range conf.Devices {
953 if configDev.Path == "/dev/ram0" {
954 found = true
955 break
956 }
957 }
958 if !found {
959 t.Errorf("device /dev/ram0 not found in config devices; got %v", conf.Devices)
960 }
961 }
962
View as plain text