This repository has no description
1package microvm
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "strings"
8 "testing"
9)
10
11func writeSpecFile(t *testing.T, path string) {
12 t.Helper()
13 data, err := json.Marshal(validImageSpec())
14 if err != nil {
15 t.Fatal(err)
16 }
17 if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
18 t.Fatal(err)
19 }
20 if err := os.WriteFile(path, data, 0o644); err != nil {
21 t.Fatal(err)
22 }
23}
24
25func TestResolveImageConventionalLayouts(t *testing.T) {
26 cases := []struct {
27 name string
28 layout func(t *testing.T, dir string)
29 }{
30 {
31 name: "directory with spec.json",
32 layout: func(t *testing.T, dir string) {
33 writeSpecFile(t, filepath.Join(dir, "nixos", "spec.json"))
34 },
35 },
36 {
37 name: "flat <name>.json",
38 layout: func(t *testing.T, dir string) {
39 writeSpecFile(t, filepath.Join(dir, "nixos.json"))
40 },
41 },
42 }
43
44 for _, tc := range cases {
45 t.Run(tc.name, func(t *testing.T) {
46 dir := t.TempDir()
47 tc.layout(t, dir)
48
49 e := testEngine(t, dir)
50 spec, path, name, err := e.resolveImage("nixos")
51 if err != nil {
52 t.Fatalf("resolveImage: %v", err)
53 }
54 if name != "nixos" {
55 t.Fatalf("name = %q, want nixos", name)
56 }
57 if !strings.HasPrefix(path, dir) {
58 t.Fatalf("resolved path %q not under image dir %q", path, dir)
59 }
60 if spec.Shell == "" {
61 t.Fatal("resolved spec not loaded")
62 }
63 })
64 }
65}
66
67func TestResolveImageDirectoryMissingSpec(t *testing.T) {
68 dir := t.TempDir()
69 if err := os.MkdirAll(filepath.Join(dir, "nixos"), 0o755); err != nil {
70 t.Fatal(err)
71 }
72
73 e := testEngine(t, dir)
74 _, _, _, err := e.resolveImage("nixos")
75 if err == nil || !strings.Contains(err.Error(), imageSpecFileName) {
76 t.Fatalf("directory without %s should error, got: %v", imageSpecFileName, err)
77 }
78}
79
80func TestResolveImageRequiresImageDirOnlyWhenUsed(t *testing.T) {
81 e := testEngine(t, "")
82 _, _, _, err := e.resolveImage("nixos")
83 if err == nil || !strings.Contains(err.Error(), "SPINDLE_MICROVM_PIPELINES_IMAGE_DIR") {
84 t.Fatalf("missing image directory should error when resolving an image, got: %v", err)
85 }
86}
87
88func TestResolveImageRejectsPaths(t *testing.T) {
89 e := testEngine(t, t.TempDir())
90 for _, name := range []string{"/etc/passwd", "../evil", "sub/evil", "..", "."} {
91 if _, _, _, err := e.resolveImage(name); err == nil || !strings.Contains(err.Error(), "must be a plain name") {
92 t.Fatalf("name %q should be rejected as a path, got: %v", name, err)
93 }
94 }
95}
96
97func validImageSpec() ImageSpec {
98 return ImageSpec{
99 Arch: "x86_64",
100 BootArgs: "console=ttyS0",
101 Initrd: "initrd",
102 Kernel: "kernel",
103 RunnerConfig: RunnerConfig{
104 Machine: "microvm",
105 },
106 MemoryMiB: 2048,
107 Shell: "/bin/sh",
108 StoreDisk: "store-disk",
109 VCPUs: 2,
110 }
111}
112
113func TestImageSpecValidateWithoutBaseConfigHash(t *testing.T) {
114 spec := validImageSpec()
115 if err := spec.Validate(); err != nil {
116 t.Fatalf("non-NixOS image spec should validate: %v", err)
117 }
118 if spec.SupportsConfigActivation() {
119 t.Fatal("spec without baseConfigHash should not support config activation")
120 }
121
122 spec.BaseConfigHash = "abcdef"
123 if !spec.SupportsConfigActivation() {
124 t.Fatal("spec with baseConfigHash should support config activation")
125 }
126}
127
128func TestImageSpecRequiresShell(t *testing.T) {
129 spec := validImageSpec()
130 spec.Shell = ""
131 err := spec.Validate()
132 if err == nil || !strings.Contains(err.Error(), "shell") {
133 t.Fatalf("spec without shell should fail validation, got: %v", err)
134 }
135}