gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/specutils/specutils_test.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package specutils 16 17 import ( 18 "fmt" 19 "os/exec" 20 "strings" 21 "testing" 22 "time" 23 24 specs "github.com/opencontainers/runtime-spec/specs-go" 25 ) 26 27 func TestWaitForReadyHappy(t *testing.T) { 28 cmd := exec.Command("/bin/sleep", "1000") 29 if err := cmd.Start(); err != nil { 30 t.Fatalf("cmd.Start() failed, err: %v", err) 31 } 32 defer func() { _ = cmd.Wait() }() 33 34 var count int 35 err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { 36 if count < 3 { 37 count++ 38 return false, nil 39 } 40 return true, nil 41 }) 42 if err != nil { 43 t.Errorf("ProcessWaitReady got: %v, expected: nil", err) 44 } 45 if err := cmd.Process.Kill(); err != nil { 46 t.Errorf("cmd.ProcessKill(): %v", err) 47 } 48 } 49 50 func TestWaitForReadyFail(t *testing.T) { 51 cmd := exec.Command("/bin/sleep", "1000") 52 if err := cmd.Start(); err != nil { 53 t.Fatalf("cmd.Start() failed, err: %v", err) 54 } 55 defer func() { _ = cmd.Wait() }() 56 57 var count int 58 err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { 59 if count < 3 { 60 count++ 61 return false, nil 62 } 63 return false, fmt.Errorf("fake error") 64 }) 65 if err == nil { 66 t.Errorf("ProcessWaitReady got: nil, expected: error") 67 } 68 if err := cmd.Process.Kill(); err != nil { 69 t.Errorf("cmd.ProcessKill(): %v", err) 70 } 71 } 72 73 func TestWaitForReadyNotRunning(t *testing.T) { 74 cmd := exec.Command("/bin/true") 75 if err := cmd.Start(); err != nil { 76 t.Fatalf("cmd.Start() failed, err: %v", err) 77 } 78 defer func() { _ = cmd.Wait() }() 79 80 err := WaitForReady(cmd.Process.Pid, 5*time.Second, func() (bool, error) { 81 return false, nil 82 }) 83 if err != nil && !strings.Contains(err.Error(), "terminated") { 84 t.Errorf("ProcessWaitReady got: %v, expected: process terminated", err) 85 } 86 if err == nil { 87 t.Errorf("ProcessWaitReady incorrectly succeeded") 88 } 89 } 90 91 func TestWaitForReadyTimeout(t *testing.T) { 92 cmd := exec.Command("/bin/sleep", "1000") 93 if err := cmd.Start(); err != nil { 94 t.Fatalf("cmd.Start() failed, err: %v", err) 95 } 96 defer func() { _ = cmd.Wait() }() 97 98 err := WaitForReady(cmd.Process.Pid, 50*time.Millisecond, func() (bool, error) { 99 return false, nil 100 }) 101 if err == nil || !strings.Contains(err.Error(), "not running yet") { 102 t.Errorf("ProcessWaitReady got: %v, expected: not running yet", err) 103 } 104 if err := cmd.Process.Kill(); err != nil { 105 t.Errorf("cmd.ProcessKill(): %v", err) 106 } 107 } 108 109 func TestSpecInvalid(t *testing.T) { 110 for _, test := range []struct { 111 name string 112 spec specs.Spec 113 error string 114 }{ 115 { 116 name: "valid", 117 spec: specs.Spec{ 118 Root: &specs.Root{Path: "/"}, 119 Process: &specs.Process{ 120 Args: []string{"/bin/true"}, 121 }, 122 Mounts: []specs.Mount{ 123 { 124 Source: "src", 125 Destination: "/dst", 126 }, 127 }, 128 }, 129 error: "", 130 }, 131 { 132 name: "valid+warning", 133 spec: specs.Spec{ 134 Root: &specs.Root{Path: "/"}, 135 Process: &specs.Process{ 136 Args: []string{"/bin/true"}, 137 // This is normally set by docker and will just cause warnings to be logged. 138 ApparmorProfile: "someprofile", 139 }, 140 // This is normally set by docker and will just cause warnings to be logged. 141 Linux: &specs.Linux{Seccomp: &specs.LinuxSeccomp{}}, 142 }, 143 error: "", 144 }, 145 { 146 name: "no root", 147 spec: specs.Spec{ 148 Process: &specs.Process{ 149 Args: []string{"/bin/true"}, 150 }, 151 }, 152 error: "must be defined", 153 }, 154 { 155 name: "empty root", 156 spec: specs.Spec{ 157 Root: &specs.Root{}, 158 Process: &specs.Process{ 159 Args: []string{"/bin/true"}, 160 }, 161 }, 162 error: "must be defined", 163 }, 164 { 165 name: "no process", 166 spec: specs.Spec{ 167 Root: &specs.Root{Path: "/"}, 168 }, 169 error: "must be defined", 170 }, 171 { 172 name: "empty args", 173 spec: specs.Spec{ 174 Root: &specs.Root{Path: "/"}, 175 Process: &specs.Process{}, 176 }, 177 error: "must be defined", 178 }, 179 { 180 name: "selinux", 181 spec: specs.Spec{ 182 Root: &specs.Root{Path: "/"}, 183 Process: &specs.Process{ 184 Args: []string{"/bin/true"}, 185 SelinuxLabel: "somelabel", 186 }, 187 }, 188 error: "is not supported", 189 }, 190 { 191 name: "solaris", 192 spec: specs.Spec{ 193 Root: &specs.Root{Path: "/"}, 194 Process: &specs.Process{ 195 Args: []string{"/bin/true"}, 196 }, 197 Solaris: &specs.Solaris{}, 198 }, 199 error: "is not supported", 200 }, 201 { 202 name: "windows", 203 spec: specs.Spec{ 204 Root: &specs.Root{Path: "/"}, 205 Process: &specs.Process{ 206 Args: []string{"/bin/true"}, 207 }, 208 Windows: &specs.Windows{}, 209 }, 210 error: "is not supported", 211 }, 212 { 213 name: "relative mount destination", 214 spec: specs.Spec{ 215 Root: &specs.Root{Path: "/"}, 216 Process: &specs.Process{ 217 Args: []string{"/bin/true"}, 218 }, 219 Mounts: []specs.Mount{ 220 { 221 Source: "src", 222 Destination: "dst", 223 }, 224 }, 225 }, 226 error: "must be an absolute path", 227 }, 228 { 229 name: "invalid mount option", 230 spec: specs.Spec{ 231 Root: &specs.Root{Path: "/"}, 232 Process: &specs.Process{ 233 Args: []string{"/bin/true"}, 234 }, 235 Mounts: []specs.Mount{ 236 { 237 Source: "/src", 238 Destination: "/dst", 239 Type: "bind", 240 Options: []string{"shared"}, 241 }, 242 }, 243 }, 244 error: "is not supported", 245 }, 246 { 247 name: "invalid rootfs propagation", 248 spec: specs.Spec{ 249 Root: &specs.Root{Path: "/"}, 250 Process: &specs.Process{ 251 Args: []string{"/bin/true"}, 252 }, 253 Linux: &specs.Linux{ 254 RootfsPropagation: "foo", 255 }, 256 }, 257 error: "root mount propagation option must specify private or slave", 258 }, 259 } { 260 err := ValidateSpec(&test.spec) 261 if len(test.error) == 0 { 262 if err != nil { 263 t.Errorf("ValidateSpec(%q) failed, err: %v", test.name, err) 264 } 265 } else { 266 if err == nil || !strings.Contains(err.Error(), test.error) { 267 t.Errorf("ValidateSpec(%q) wrong error, got: %v, want: .*%s.*", test.name, err, test.error) 268 } 269 } 270 } 271 } 272 273 func TestSeccomp(t *testing.T) { 274 const containerName = "cont1" 275 for _, tc := range []struct { 276 name string 277 spec specs.Spec 278 seccompPresent bool 279 }{ 280 { 281 name: "seccomp set", 282 seccompPresent: true, 283 spec: specs.Spec{ 284 Annotations: map[string]string{ 285 annotationContainerName: containerName, 286 }, 287 Linux: &specs.Linux{ 288 Seccomp: &specs.LinuxSeccomp{}, 289 }, 290 }, 291 }, 292 { 293 name: "another container", 294 seccompPresent: true, 295 spec: specs.Spec{ 296 Annotations: map[string]string{ 297 annotationContainerName: containerName, 298 annotationSeccomp + "cont2": annotationSeccompRuntimeDefault, 299 }, 300 Linux: &specs.Linux{ 301 Seccomp: &specs.LinuxSeccomp{}, 302 }, 303 }, 304 }, 305 { 306 name: "not RuntimeDefault", 307 seccompPresent: true, 308 spec: specs.Spec{ 309 Annotations: map[string]string{ 310 annotationContainerName: containerName, 311 annotationSeccomp + containerName: "foobar", 312 }, 313 Linux: &specs.Linux{ 314 Seccomp: &specs.LinuxSeccomp{}, 315 }, 316 }, 317 }, 318 { 319 name: "not RuntimeDefault many names", 320 seccompPresent: true, 321 spec: specs.Spec{ 322 Annotations: map[string]string{ 323 annotationContainerName: containerName, 324 annotationSeccomp + containerName: "foobar", 325 annotationSeccomp + "cont2": annotationSeccompRuntimeDefault, 326 }, 327 Linux: &specs.Linux{ 328 Seccomp: &specs.LinuxSeccomp{}, 329 }, 330 }, 331 }, 332 { 333 name: "remove", 334 spec: specs.Spec{ 335 Annotations: map[string]string{ 336 annotationContainerName: containerName, 337 annotationSeccomp + containerName: annotationSeccompRuntimeDefault, 338 }, 339 Linux: &specs.Linux{ 340 Seccomp: &specs.LinuxSeccomp{}, 341 }, 342 }, 343 }, 344 { 345 name: "remove many names", 346 spec: specs.Spec{ 347 Annotations: map[string]string{ 348 annotationContainerName: containerName, 349 annotationSeccomp + containerName: annotationSeccompRuntimeDefault, 350 annotationSeccomp + "cont2": "foobar", 351 }, 352 Linux: &specs.Linux{ 353 Seccomp: &specs.LinuxSeccomp{}, 354 }, 355 }, 356 }, 357 { 358 name: "remove-nonexistent", 359 spec: specs.Spec{ 360 Annotations: map[string]string{ 361 annotationSeccomp + containerName: annotationSeccompRuntimeDefault, 362 }, 363 }, 364 }, 365 { 366 name: "empty", 367 }, 368 } { 369 t.Run(tc.name, func(t *testing.T) { 370 tc.spec.Root = &specs.Root{} 371 fixSpec(&tc.spec, "", nil) 372 if tc.seccompPresent { 373 if tc.spec.Linux == nil || tc.spec.Linux.Seccomp == nil { 374 t.Errorf("seccomp is not in the spec: %+v", tc.spec) 375 } 376 } else if tc.spec.Linux != nil && tc.spec.Linux.Seccomp != nil { 377 t.Errorf("seccomp is in the spec: %+v", tc.spec) 378 } 379 }) 380 } 381 }