github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/worker/runtime/spec/spec_test.go (about) 1 package spec_test 2 3 import ( 4 "testing" 5 6 "code.cloudfoundry.org/garden" 7 "github.com/pf-qiu/concourse/v6/worker/runtime/spec" 8 specs "github.com/opencontainers/runtime-spec/specs-go" 9 "github.com/stretchr/testify/require" 10 "github.com/stretchr/testify/suite" 11 ) 12 13 var ( 14 dummyMaxUid uint32 = 0 15 dummyMaxGid uint32 = 0 16 ) 17 18 type SpecSuite struct { 19 suite.Suite 20 *require.Assertions 21 } 22 23 func uint64Ptr(i uint64) *uint64 { return &i } 24 func int64Ptr(i int64) *int64 { return &i } 25 26 func (s *SpecSuite) TestContainerSpecValidations() { 27 for _, tc := range []struct { 28 desc string 29 spec garden.ContainerSpec 30 }{ 31 { 32 desc: "no handle specified", 33 spec: garden.ContainerSpec{}, 34 }, 35 { 36 desc: "rootfsPath not specified", 37 spec: garden.ContainerSpec{ 38 Handle: "handle", 39 }, 40 }, 41 { 42 desc: "rootfsPath without scheme", 43 spec: garden.ContainerSpec{ 44 Handle: "handle", 45 RootFSPath: "foo", 46 }, 47 }, 48 { 49 desc: "rootfsPath with unknown scheme", 50 spec: garden.ContainerSpec{ 51 Handle: "handle", 52 RootFSPath: "weird://foo", 53 }, 54 }, 55 { 56 desc: "rootfsPath not being absolute", 57 spec: garden.ContainerSpec{ 58 Handle: "handle", 59 RootFSPath: "raw://../not/absolute/at/all", 60 }, 61 }, 62 { 63 desc: "both rootfsPath and image specified", 64 spec: garden.ContainerSpec{ 65 Handle: "handle", 66 RootFSPath: "foo", 67 Image: garden.ImageRef{URI: "bar"}, 68 }, 69 }, 70 { 71 desc: "no rootfsPath, but image specified w/out scheme", 72 spec: garden.ContainerSpec{ 73 Handle: "handle", 74 Image: garden.ImageRef{URI: "bar"}, 75 }, 76 }, 77 { 78 desc: "no rootfsPath, but image specified w/ unknown scheme", 79 spec: garden.ContainerSpec{ 80 Handle: "handle", 81 Image: garden.ImageRef{URI: "weird://bar"}, 82 }, 83 }, 84 } { 85 s.T().Run(tc.desc, func(t *testing.T) { 86 _, err := spec.OciSpec(spec.DefaultInitBinPath, tc.spec, dummyMaxUid, dummyMaxGid) 87 s.Error(err) 88 }) 89 } 90 } 91 92 func (s *SpecSuite) TestIDMappings() { 93 // TODO 94 // 95 // ensure that we mutate the right thing 96 } 97 98 func (s *SpecSuite) TestOciSpecBindMounts() { 99 for _, tc := range []struct { 100 desc string 101 mounts []garden.BindMount 102 expected []specs.Mount 103 succeeds bool 104 }{ 105 { 106 desc: "unknown mode", 107 succeeds: false, 108 mounts: []garden.BindMount{ 109 { 110 SrcPath: "/a", 111 DstPath: "/b", 112 Mode: 123, 113 Origin: garden.BindMountOriginHost, 114 }, 115 }, 116 }, 117 { 118 desc: "unknown origin", 119 succeeds: false, 120 mounts: []garden.BindMount{ 121 { 122 SrcPath: "/a", 123 DstPath: "/b", 124 Mode: garden.BindMountModeRO, 125 Origin: 123, 126 }, 127 }, 128 }, 129 { 130 desc: "w/out src", 131 succeeds: false, 132 mounts: []garden.BindMount{ 133 { 134 DstPath: "/b", 135 Mode: garden.BindMountModeRO, 136 Origin: garden.BindMountOriginHost, 137 }, 138 }, 139 }, 140 { 141 desc: "non-absolute src", 142 succeeds: false, 143 mounts: []garden.BindMount{ 144 { 145 DstPath: "/b", 146 Mode: garden.BindMountModeRO, 147 Origin: garden.BindMountOriginHost, 148 }, 149 }, 150 }, 151 { 152 desc: "w/out dest", 153 succeeds: false, 154 mounts: []garden.BindMount{ 155 { 156 SrcPath: "/a", 157 Mode: garden.BindMountModeRO, 158 Origin: garden.BindMountOriginHost, 159 }, 160 }, 161 }, 162 { 163 desc: "non-absolute dest", 164 succeeds: false, 165 mounts: []garden.BindMount{ 166 { 167 DstPath: "/b", 168 Mode: garden.BindMountModeRO, 169 Origin: garden.BindMountOriginHost, 170 }, 171 }, 172 }, 173 } { 174 s.T().Run(tc.desc, func(t *testing.T) { 175 actual, err := spec.OciSpecBindMounts(tc.mounts) 176 if !tc.succeeds { 177 s.Error(err) 178 return 179 } 180 181 s.NoError(err) 182 s.Equal(tc.expected, actual) 183 }) 184 } 185 } 186 187 func (s *SpecSuite) TestOciNamespaces() { 188 for _, tc := range []struct { 189 desc string 190 privileged bool 191 expected []specs.LinuxNamespace 192 }{ 193 { 194 desc: "privileged", 195 privileged: true, 196 expected: spec.PrivilegedContainerNamespaces, 197 }, 198 { 199 desc: "unprivileged", 200 privileged: false, 201 expected: spec.UnprivilegedContainerNamespaces, 202 }, 203 } { 204 s.T().Run(tc.desc, func(t *testing.T) { 205 s.Equal(tc.expected, spec.OciNamespaces(tc.privileged)) 206 }) 207 } 208 } 209 210 func (s *SpecSuite) TestOciCapabilities() { 211 for _, tc := range []struct { 212 desc string 213 privileged bool 214 expected specs.LinuxCapabilities 215 }{ 216 { 217 desc: "privileged", 218 privileged: true, 219 expected: spec.PrivilegedContainerCapabilities, 220 }, 221 { 222 desc: "unprivileged", 223 privileged: false, 224 expected: spec.UnprivilegedContainerCapabilities, 225 }, 226 } { 227 s.T().Run(tc.desc, func(t *testing.T) { 228 s.Equal(tc.expected, spec.OciCapabilities(tc.privileged)) 229 }) 230 } 231 } 232 233 func (s *SpecSuite) TestOciResourceLimits() { 234 for _, tc := range []struct { 235 desc string 236 limits garden.Limits 237 expected *specs.LinuxResources 238 }{ 239 { 240 desc: "CPU limit in weight", 241 limits: garden.Limits{ 242 CPU: garden.CPULimits{ 243 Weight: 512, 244 }, 245 }, 246 expected: &specs.LinuxResources{ 247 CPU: &specs.LinuxCPU{ 248 Shares: uint64Ptr(512), 249 }, 250 }, 251 }, 252 { 253 desc: "CPU limit in shares", 254 limits: garden.Limits{ 255 CPU: garden.CPULimits{ 256 LimitInShares: 512, 257 }, 258 }, 259 expected: &specs.LinuxResources{ 260 CPU: &specs.LinuxCPU{ 261 Shares: uint64Ptr(512), 262 }, 263 }, 264 }, 265 { 266 desc: "CPU limit prefers weight", 267 limits: garden.Limits{ 268 CPU: garden.CPULimits{ 269 LimitInShares: 512, 270 Weight: 1024, 271 }, 272 }, 273 expected: &specs.LinuxResources{ 274 CPU: &specs.LinuxCPU{ 275 Shares: uint64Ptr(1024), 276 }, 277 }, 278 }, 279 { 280 desc: "Memory limit", 281 limits: garden.Limits{ 282 Memory: garden.MemoryLimits{ 283 LimitInBytes: 10000, 284 }, 285 }, 286 expected: &specs.LinuxResources{ 287 Memory: &specs.LinuxMemory{ 288 Limit: int64Ptr(10000), 289 Swap: int64Ptr(10000), 290 }, 291 }, 292 }, 293 { 294 desc: "PID limit", 295 limits: garden.Limits{ 296 Pid: garden.PidLimits { 297 Max: 1000, 298 }, 299 }, 300 expected: &specs.LinuxResources{ 301 Pids: &specs.LinuxPids{ 302 Limit: 1000, 303 }, 304 }, 305 }, 306 { 307 desc: "No limits specified", 308 limits: garden.Limits{}, 309 expected: nil, 310 }, 311 } { 312 s.T().Run(tc.desc, func(t *testing.T) { 313 s.Equal(tc.expected, spec.OciResources(tc.limits)) 314 }) 315 } 316 } 317 318 func (s *SpecSuite) TestOciCgroupsPath() { 319 for _, tc := range []struct { 320 desc string 321 basePath string 322 handle string 323 privileged bool 324 expected string 325 }{ 326 { 327 desc: "not privileged", 328 basePath: "garden", 329 handle: "1234", 330 privileged: false, 331 expected: "garden/1234", 332 }, 333 { 334 desc: "privileged", 335 basePath: "garden", 336 handle: "1234", 337 privileged: true, 338 expected: "", 339 }, 340 } { 341 s.T().Run(tc.desc, func(t *testing.T) { 342 s.Equal(tc.expected, spec.OciCgroupsPath(tc.basePath, tc.handle, tc.privileged)) 343 }) 344 } 345 } 346 347 func (s *SpecSuite) TestContainerSpec() { 348 var minimalContainerSpec = garden.ContainerSpec{ 349 Handle: "handle", RootFSPath: "raw:///rootfs", 350 } 351 352 for _, tc := range []struct { 353 desc string 354 gdn garden.ContainerSpec 355 check func(*specs.Spec) 356 }{ 357 { 358 desc: "defaults", 359 gdn: minimalContainerSpec, 360 check: func(oci *specs.Spec) { 361 s.Equal("/", oci.Process.Cwd) 362 s.Equal([]string{"/tmp/gdn-init"}, oci.Process.Args) 363 s.Equal(oci.Mounts, spec.AnyContainerMounts(spec.DefaultInitBinPath)) 364 365 s.Equal(minimalContainerSpec.Handle, oci.Hostname) 366 s.Equal(spec.AnyContainerDevices, oci.Linux.Resources.Devices) 367 }, 368 }, 369 { 370 desc: "default devices privileged", 371 gdn: garden.ContainerSpec{ 372 Handle: "handle", RootFSPath: "raw:///rootfs", 373 Privileged: true, 374 }, 375 check: func(oci *specs.Spec) { 376 s.Equal(append(spec.PrivilegedOnlyDevices, spec.AnyContainerDevices...), oci.Linux.Resources.Devices) 377 }, 378 }, 379 { 380 desc: "env + default path", 381 gdn: garden.ContainerSpec{ 382 Handle: "handle", RootFSPath: "raw:///rootfs", 383 Env: []string{"foo=bar"}, 384 }, 385 check: func(oci *specs.Spec) { 386 s.Equal([]string{"foo=bar", spec.Path}, oci.Process.Env) 387 }, 388 }, 389 { 390 desc: "env + default root path", 391 gdn: garden.ContainerSpec{ 392 Handle: "handle", RootFSPath: "raw:///rootfs", 393 Env: []string{"foo=bar"}, 394 Privileged: true, 395 }, 396 check: func(oci *specs.Spec) { 397 s.Equal([]string{"foo=bar", spec.SuperuserPath}, oci.Process.Env) 398 }, 399 }, 400 { 401 desc: "env with path already configured", 402 gdn: garden.ContainerSpec{ 403 Handle: "handle", RootFSPath: "raw:///rootfs", 404 Env: []string{"foo=bar", "PATH=/somewhere"}, 405 }, 406 check: func(oci *specs.Spec) { 407 s.Equal([]string{"foo=bar", "PATH=/somewhere"}, oci.Process.Env) 408 }, 409 }, 410 { 411 desc: "mounts", 412 gdn: garden.ContainerSpec{ 413 Handle: "handle", RootFSPath: "raw:///rootfs", 414 BindMounts: []garden.BindMount{ 415 { // ro mount 416 SrcPath: "/a", 417 DstPath: "/b", 418 Mode: garden.BindMountModeRO, 419 Origin: garden.BindMountOriginHost, 420 }, 421 { // rw mount 422 SrcPath: "/a", 423 DstPath: "/b", 424 Mode: garden.BindMountModeRW, 425 Origin: garden.BindMountOriginHost, 426 }, 427 }, 428 }, 429 check: func(oci *specs.Spec) { 430 s.Contains(oci.Mounts, specs.Mount{ 431 Source: "/a", 432 Destination: "/b", 433 Type: "bind", 434 Options: []string{"bind", "ro"}, 435 }) 436 s.Contains(oci.Mounts, specs.Mount{ 437 Source: "/a", 438 Destination: "/b", 439 Type: "bind", 440 Options: []string{"bind", "rw"}, 441 }) 442 }, 443 }, 444 { 445 desc: "seccomp is not empty for unprivileged", 446 gdn: garden.ContainerSpec{ 447 Handle: "handle", RootFSPath: "raw:///rootfs", 448 Privileged: false, 449 }, 450 check: func(oci *specs.Spec) { 451 s.NotEmpty(oci.Linux.Seccomp) 452 }, 453 }, 454 { 455 desc: "seccomp is empty for privileged", 456 gdn: garden.ContainerSpec{ 457 Handle: "handle", RootFSPath: "raw:///rootfs", 458 Privileged: true, 459 }, 460 check: func(oci *specs.Spec) { 461 s.Empty(oci.Linux.Seccomp) 462 }, 463 }, 464 { 465 desc: "limits", 466 gdn: garden.ContainerSpec{ 467 Handle: "handle", RootFSPath: "raw:///rootfs", 468 Limits: garden.Limits{ 469 CPU: garden.CPULimits{ 470 Weight: 512, 471 }, 472 Memory: garden.MemoryLimits{ 473 LimitInBytes: 10000, 474 }, 475 Pid: garden.PidLimits{ 476 Max: 1000, 477 }, 478 }, 479 }, 480 check: func(oci *specs.Spec) { 481 s.NotNil(oci.Linux.Resources.CPU) 482 s.Equal(uint64Ptr(512), oci.Linux.Resources.CPU.Shares) 483 s.NotNil(oci.Linux.Resources.Memory) 484 s.Equal(int64Ptr(10000), oci.Linux.Resources.Memory.Limit) 485 s.NotNil(oci.Linux.Resources.Pids) 486 s.Equal(int64(1000), oci.Linux.Resources.Pids.Limit) 487 488 s.NotNil(oci.Linux.Resources.Devices) 489 }, 490 }, 491 { 492 desc: "cgroups path", 493 gdn: garden.ContainerSpec{ 494 Handle: "handle", RootFSPath: "raw:///rootfs", 495 Privileged: false, 496 }, 497 check: func(oci *specs.Spec) { 498 s.Equal("garden/handle", oci.Linux.CgroupsPath) 499 }, 500 }, 501 } { 502 s.T().Run(tc.desc, func(t *testing.T) { 503 actual, err := spec.OciSpec(spec.DefaultInitBinPath, tc.gdn, dummyMaxUid, dummyMaxGid) 504 s.NoError(err) 505 506 tc.check(actual) 507 }) 508 } 509 }