github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/worker/runtime/container_test.go (about) 1 package runtime_test 2 3 import ( 4 "errors" 5 6 "code.cloudfoundry.org/garden" 7 "github.com/pf-qiu/concourse/v6/worker/runtime" 8 "github.com/pf-qiu/concourse/v6/worker/runtime/libcontainerd/libcontainerdfakes" 9 "github.com/pf-qiu/concourse/v6/worker/runtime/runtimefakes" 10 "github.com/containerd/containerd" 11 "github.com/opencontainers/runtime-spec/specs-go" 12 "github.com/stretchr/testify/require" 13 "github.com/stretchr/testify/suite" 14 ) 15 16 type ContainerSuite struct { 17 suite.Suite 18 *require.Assertions 19 20 container *runtime.Container 21 containerdContainer *libcontainerdfakes.FakeContainer 22 containerdProcess *libcontainerdfakes.FakeProcess 23 containerdTask *libcontainerdfakes.FakeTask 24 rootfsManager *runtimefakes.FakeRootfsManager 25 killer *runtimefakes.FakeKiller 26 } 27 28 func (s *ContainerSuite) SetupTest() { 29 s.containerdContainer = new(libcontainerdfakes.FakeContainer) 30 s.containerdProcess = new(libcontainerdfakes.FakeProcess) 31 s.containerdTask = new(libcontainerdfakes.FakeTask) 32 s.rootfsManager = new(runtimefakes.FakeRootfsManager) 33 s.killer = new(runtimefakes.FakeKiller) 34 35 s.container = runtime.NewContainer( 36 s.containerdContainer, 37 s.killer, 38 s.rootfsManager, 39 ) 40 } 41 42 // func (s *ContainerSuite) TestStopWithKillUngracefullyStops() { 43 // err := s.container.Stop(true) 44 // s.NoError(err) 45 // s.Equal(1, s.ungracefulKiller.KillCallCount()) 46 // } 47 48 // func (s *ContainerSuite) TestStopWithKillFailing() { 49 // s.ungracefulKiller.UngracefullyStopReturns(errors.NewGardenBackend("ungraceful-stop-err")) 50 51 // err := s.container.Stop(true) 52 // s.Equal(1, s.ungracefulKiller.UngracefullyStopCallCount()) 53 // s.EqualError(errors.Unwrap(err), "ungraceful-stop-err") 54 // } 55 56 // func (s *ContainerSuite) TestStopWithoutKillGracefullyStops() { 57 // err := s.container.Stop(false) 58 // s.NoError(err) 59 // s.Equal(1, s.ungracefulKiller.GracefullyStopCallCount()) 60 // } 61 62 // func (s *ContainerSuite) TestStopWithoutKillFailing() { 63 // s.ungracefulKiller.GracefullyStopReturns(errors.NewGardenBackend("graceful-stop-err")) 64 65 // err := s.container.Stop(false) 66 // s.EqualError(errors.Unwrap(err), "graceful-stop-err") 67 // s.Equal(1, s.ungracefulKiller.GracefullyStopCallCount()) 68 // } 69 70 func (s *ContainerSuite) TestRunContainerSpecErr() { 71 expectedErr := errors.New("spec-err") 72 s.containerdContainer.SpecReturns(nil, expectedErr) 73 74 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 75 s.True(errors.Is(err, expectedErr)) 76 } 77 78 func (s *ContainerSuite) TestRunWithNonRootCwdSetupCwdFails() { 79 s.containerdContainer.SpecReturns(&specs.Spec{ 80 Process: &specs.Process{}, 81 Root: &specs.Root{}, 82 }, nil) 83 84 expectedErr := errors.New("setup-cwd-err") 85 s.rootfsManager.SetupCwdReturns(expectedErr) 86 87 _, err := s.container.Run(garden.ProcessSpec{Dir: "/somewhere"}, garden.ProcessIO{}) 88 s.True(errors.Is(err, expectedErr)) 89 } 90 91 func (s *ContainerSuite) TestRunTaskError() { 92 s.containerdContainer.SpecReturns(&specs.Spec{ 93 Process: &specs.Process{}, 94 Root: &specs.Root{}, 95 }, nil) 96 97 expectedErr := errors.New("task-err") 98 s.containerdContainer.TaskReturns(nil, expectedErr) 99 100 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 101 s.True(errors.Is(err, expectedErr)) 102 } 103 104 func (s *ContainerSuite) TestRunTaskExecError() { 105 s.containerdContainer.SpecReturns(&specs.Spec{ 106 Process: &specs.Process{}, 107 Root: &specs.Root{}, 108 }, nil) 109 110 s.containerdContainer.TaskReturns(s.containerdTask, nil) 111 112 expectedErr := errors.New("exec-err") 113 s.containerdTask.ExecReturns(nil, expectedErr) 114 115 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 116 s.True(errors.Is(err, expectedErr)) 117 } 118 119 func (s *ContainerSuite) TestRunProcWaitError() { 120 s.containerdContainer.SpecReturns(&specs.Spec{ 121 Process: &specs.Process{}, 122 Root: &specs.Root{}, 123 }, nil) 124 125 s.containerdContainer.TaskReturns(s.containerdTask, nil) 126 s.containerdTask.ExecReturns(s.containerdProcess, nil) 127 128 expectedErr := errors.New("proc-wait-err") 129 s.containerdProcess.WaitReturns(nil, expectedErr) 130 131 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 132 s.True(errors.Is(err, expectedErr)) 133 } 134 135 func (s *ContainerSuite) TestRunProcStartError() { 136 s.containerdContainer.SpecReturns(&specs.Spec{ 137 Process: &specs.Process{}, 138 Root: &specs.Root{}, 139 }, nil) 140 141 s.containerdContainer.TaskReturns(s.containerdTask, nil) 142 s.containerdTask.ExecReturns(s.containerdProcess, nil) 143 144 expectedErr := errors.New("proc-start-err") 145 s.containerdProcess.StartReturns(expectedErr) 146 147 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 148 s.True(errors.Is(err, expectedErr)) 149 } 150 151 func (s *ContainerSuite) TestRunProcStartErrorExecutableNotFound() { 152 s.containerdContainer.SpecReturns(&specs.Spec{ 153 Process: &specs.Process{}, 154 Root: &specs.Root{}, 155 }, nil) 156 157 s.containerdContainer.TaskReturns(s.containerdTask, nil) 158 s.containerdTask.ExecReturns(s.containerdProcess, nil) 159 160 exeNotFoundErr := errors.New("OCI runtime exec failed: exec failed: container_linux.go:345: starting container process caused: exec: potato: executable file not found in $PATH") 161 s.containerdProcess.StartReturns(exeNotFoundErr) 162 163 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 164 s.True(errors.Is(err, garden.ExecutableNotFoundError{Message: exeNotFoundErr.Error()})) 165 } 166 167 func (s *ContainerSuite) TestRunProcCloseIOError() { 168 s.containerdContainer.SpecReturns(&specs.Spec{ 169 Process: &specs.Process{}, 170 Root: &specs.Root{}, 171 }, nil) 172 173 s.containerdContainer.TaskReturns(s.containerdTask, nil) 174 s.containerdTask.ExecReturns(s.containerdProcess, nil) 175 176 expectedErr := errors.New("proc-closeio-err") 177 s.containerdProcess.CloseIOReturns(expectedErr) 178 179 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 180 s.True(errors.Is(err, expectedErr)) 181 } 182 183 func (s *ContainerSuite) TestRunProcCloseIOWithStdin() { 184 s.containerdContainer.SpecReturns(&specs.Spec{ 185 Process: &specs.Process{}, 186 Root: &specs.Root{}, 187 }, nil) 188 189 s.containerdContainer.TaskReturns(s.containerdTask, nil) 190 s.containerdTask.ExecReturns(s.containerdProcess, nil) 191 192 _, err := s.container.Run(garden.ProcessSpec{}, garden.ProcessIO{}) 193 s.NoError(err) 194 195 s.Equal(1, s.containerdProcess.CloseIOCallCount()) 196 _, opts := s.containerdProcess.CloseIOArgsForCall(0) 197 s.Len(opts, 1) 198 199 // you can't compare two functions in Go, so, compare its effects (these 200 // are functional opts). 201 // 202 obj := containerd.IOCloseInfo{} 203 opts[0](&obj) 204 205 // we want to make sure that we're passing an opt that sets `Stdin` to 206 // true on an `IOCloseInfo`. 207 s.True(obj.Stdin) 208 } 209 210 func (s *ContainerSuite) TestRunWithUserLookupSucceeds() { 211 s.containerdContainer.SpecReturns(&specs.Spec{ 212 Process: &specs.Process{}, 213 Root: &specs.Root{}, 214 }, nil) 215 216 s.containerdContainer.TaskReturns(s.containerdTask, nil) 217 s.containerdTask.ExecReturns(s.containerdProcess, nil) 218 219 expectedUser := specs.User{UID: 1, GID: 2, Username: "some_user"} 220 s.rootfsManager.LookupUserReturns(expectedUser, true, nil) 221 222 _, err := s.container.Run(garden.ProcessSpec{User: "some_user"}, garden.ProcessIO{}) 223 s.NoError(err) 224 225 _, _, procSpec, _ := s.containerdTask.ExecArgsForCall(0) 226 s.Equal(expectedUser, procSpec.User) 227 228 userEnvVarSet := false 229 expectedEnvVar := "USER=some_user" 230 231 for _, envVar := range procSpec.Env { 232 if envVar == expectedEnvVar { 233 userEnvVarSet = true 234 break 235 } 236 } 237 s.True(userEnvVarSet) 238 } 239 240 func (s *ContainerSuite) TestRunWithUserLookupErrors() { 241 s.containerdContainer.SpecReturns(&specs.Spec{ 242 Process: &specs.Process{}, 243 Root: &specs.Root{}, 244 }, nil) 245 246 s.containerdContainer.TaskReturns(s.containerdTask, nil) 247 s.containerdTask.ExecReturns(s.containerdProcess, nil) 248 249 expectedErr := errors.New("lookup error") 250 s.rootfsManager.LookupUserReturns(specs.User{}, false, expectedErr) 251 252 _, err := s.container.Run(garden.ProcessSpec{User: "some_user"}, garden.ProcessIO{}) 253 s.True(errors.Is(err, expectedErr)) 254 } 255 256 func (s *ContainerSuite) TestRunWithUserLookupNotFound() { 257 s.containerdContainer.SpecReturns(&specs.Spec{ 258 Process: &specs.Process{}, 259 Root: &specs.Root{}, 260 }, nil) 261 262 s.containerdContainer.TaskReturns(s.containerdTask, nil) 263 s.containerdTask.ExecReturns(s.containerdProcess, nil) 264 265 s.rootfsManager.LookupUserReturns(specs.User{}, false, nil) 266 267 _, err := s.container.Run(garden.ProcessSpec{User: "some_invalid_user"}, garden.ProcessIO{}) 268 s.True(errors.Is(err, runtime.UserNotFoundError{User: "some_invalid_user"})) 269 } 270 271 func (s *ContainerSuite) TestSetGraceTimeSetLabelsFails() { 272 expectedErr := errors.New("set-label-error") 273 s.containerdContainer.SetLabelsReturns(nil, expectedErr) 274 275 err := s.container.SetGraceTime(1234) 276 s.True(errors.Is(err, expectedErr)) 277 } 278 279 func (s *ContainerSuite) TestSetGraceTimeSetLabelsSucceeds() { 280 err := s.container.SetGraceTime(1234) 281 s.NoError(err) 282 283 expectedLabelSet := map[string]string{ 284 "garden.grace-time": "1234", 285 } 286 _, labelSet := s.containerdContainer.SetLabelsArgsForCall(0) 287 s.Equal(expectedLabelSet, labelSet) 288 } 289 290 func (s *ContainerSuite) TestPropertyGetLabelsFails() { 291 expectedErr := errors.New("get-labels-error") 292 s.containerdContainer.LabelsReturns(nil, expectedErr) 293 _, err := s.container.Property("any") 294 s.True(errors.Is(err, expectedErr)) 295 } 296 297 func (s *ContainerSuite) TestPropertyNotFound() { 298 s.containerdContainer.LabelsReturns(garden.Properties{}, nil) 299 _, err := s.container.Property("any") 300 s.Equal(runtime.ErrNotFound("any"), err) 301 } 302 303 func (s *ContainerSuite) TestPropertyReturnsValue() { 304 properties := garden.Properties{ 305 "any": "some-value", 306 } 307 s.containerdContainer.LabelsReturns(properties, nil) 308 result, err := s.container.Property("any") 309 s.NoError(err) 310 s.Equal("some-value", result) 311 } 312 313 func (s *ContainerSuite) TestCurrentCPULimitsGetInfoFails() { 314 expectedErr := errors.New("get-spec-error") 315 s.containerdContainer.SpecReturns(nil, expectedErr) 316 _, err := s.container.CurrentCPULimits() 317 s.True(errors.Is(err, expectedErr)) 318 } 319 320 func (s *ContainerSuite) TestCurrentCPULimitsNoLimitSet() { 321 s.containerdContainer.SpecReturns(&specs.Spec{ 322 Linux: &specs.Linux{ 323 Resources: &specs.LinuxResources{ 324 CPU: &specs.LinuxCPU{}, 325 }, 326 }, 327 }, nil) 328 limits, err := s.container.CurrentCPULimits() 329 s.NoError(err) 330 s.Equal(garden.CPULimits{}, limits) 331 } 332 333 func (s *ContainerSuite) TestCurrentCPULimitsReturnsCPUShares() { 334 cpuShares := uint64(512) 335 s.containerdContainer.SpecReturns(&specs.Spec{ 336 Linux: &specs.Linux{ 337 Resources: &specs.LinuxResources{ 338 CPU: &specs.LinuxCPU{ 339 Shares: &cpuShares, 340 }, 341 }, 342 }, 343 }, nil) 344 limits, err := s.container.CurrentCPULimits() 345 s.NoError(err) 346 s.Equal(garden.CPULimits{Weight: cpuShares}, limits) 347 } 348 349 func (s *ContainerSuite) TestCurrentMemoryLimitsGetSpecFails() { 350 expectedErr := errors.New("get-spec-error") 351 s.containerdContainer.SpecReturns(nil, expectedErr) 352 _, err := s.container.CurrentMemoryLimits() 353 s.True(errors.Is(err, expectedErr)) 354 } 355 356 func (s *ContainerSuite) TestCurrentMemoryLimitsNoLimitSet() { 357 s.containerdContainer.SpecReturns(&specs.Spec{ 358 Linux: &specs.Linux{ 359 Resources: &specs.LinuxResources{ 360 Memory: &specs.LinuxMemory{}, 361 }, 362 }, 363 }, nil) 364 limits, err := s.container.CurrentMemoryLimits() 365 s.NoError(err) 366 s.Equal(garden.MemoryLimits{}, limits) 367 } 368 369 func (s *ContainerSuite) TestCurrentMemoryLimitsReturnsLimit() { 370 limitBytes := int64(512) 371 s.containerdContainer.SpecReturns(&specs.Spec{ 372 Linux: &specs.Linux{ 373 Resources: &specs.LinuxResources{ 374 Memory: &specs.LinuxMemory{ 375 Limit: &limitBytes, 376 }, 377 }, 378 }, 379 }, nil) 380 limits, err := s.container.CurrentMemoryLimits() 381 s.NoError(err) 382 s.Equal(garden.MemoryLimits{LimitInBytes: uint64(limitBytes)}, limits) 383 }