1package worker_test 2 3import ( 4 "code.cloudfoundry.org/lager" 5 "code.cloudfoundry.org/lager/lagertest" 6 "github.com/concourse/concourse/atc/db" 7 . "github.com/concourse/concourse/atc/worker" 8 "github.com/concourse/concourse/atc/worker/workerfakes" 9 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12) 13 14//go:generate counterfeiter . ContainerPlacementStrategy 15 16var ( 17 strategy ContainerPlacementStrategy 18 19 spec ContainerSpec 20 metadata db.ContainerMetadata 21 workers []Worker 22 23 chosenWorker Worker 24 chooseErr error 25 26 compatibleWorkerOneCache1 *workerfakes.FakeWorker 27 compatibleWorkerOneCache2 *workerfakes.FakeWorker 28 compatibleWorkerTwoCaches *workerfakes.FakeWorker 29 compatibleWorkerNoCaches1 *workerfakes.FakeWorker 30 compatibleWorkerNoCaches2 *workerfakes.FakeWorker 31 32 logger *lagertest.TestLogger 33) 34 35var _ = Describe("FewestBuildContainersPlacementStrategy", func() { 36 Describe("Choose", func() { 37 var compatibleWorker1 *workerfakes.FakeWorker 38 var compatibleWorker2 *workerfakes.FakeWorker 39 var compatibleWorker3 *workerfakes.FakeWorker 40 41 BeforeEach(func() { 42 logger = lagertest.NewTestLogger("build-containers-equal-placement-test") 43 strategy = NewFewestBuildContainersPlacementStrategy() 44 compatibleWorker1 = new(workerfakes.FakeWorker) 45 compatibleWorker2 = new(workerfakes.FakeWorker) 46 compatibleWorker3 = new(workerfakes.FakeWorker) 47 48 spec = ContainerSpec{ 49 ImageSpec: ImageSpec{ResourceType: "some-type"}, 50 51 TeamID: 4567, 52 53 Inputs: []InputSource{}, 54 } 55 }) 56 57 Context("when there is only one worker", func() { 58 BeforeEach(func() { 59 workers = []Worker{compatibleWorker1} 60 compatibleWorker1.BuildContainersReturns(20) 61 }) 62 63 It("picks that worker", func() { 64 chosenWorker, chooseErr = strategy.Choose( 65 logger, 66 workers, 67 spec, 68 ) 69 Expect(chooseErr).ToNot(HaveOccurred()) 70 Expect(chosenWorker).To(Equal(compatibleWorker1)) 71 }) 72 }) 73 74 Context("when there are multiple workers", func() { 75 BeforeEach(func() { 76 workers = []Worker{compatibleWorker1, compatibleWorker2, compatibleWorker3} 77 78 compatibleWorker1.BuildContainersReturns(30) 79 compatibleWorker2.BuildContainersReturns(20) 80 compatibleWorker3.BuildContainersReturns(10) 81 }) 82 83 Context("when the container is not of type 'check'", func() { 84 It("picks the one with least amount of containers", func() { 85 Consistently(func() Worker { 86 chosenWorker, chooseErr = strategy.Choose( 87 logger, 88 workers, 89 spec, 90 ) 91 Expect(chooseErr).ToNot(HaveOccurred()) 92 return chosenWorker 93 }).Should(Equal(compatibleWorker3)) 94 }) 95 96 Context("when there is more than one worker with the same number of build containers", func() { 97 BeforeEach(func() { 98 workers = []Worker{compatibleWorker1, compatibleWorker2, compatibleWorker3} 99 compatibleWorker1.BuildContainersReturns(10) 100 }) 101 102 It("picks any of them", func() { 103 Consistently(func() Worker { 104 chosenWorker, chooseErr = strategy.Choose( 105 logger, 106 workers, 107 spec, 108 ) 109 Expect(chooseErr).ToNot(HaveOccurred()) 110 return chosenWorker 111 }).Should(Or(Equal(compatibleWorker1), Equal(compatibleWorker3))) 112 }) 113 }) 114 115 }) 116 }) 117 }) 118}) 119 120var _ = Describe("VolumeLocalityPlacementStrategy", func() { 121 Describe("Choose", func() { 122 JustBeforeEach(func() { 123 chosenWorker, chooseErr = strategy.Choose( 124 logger, 125 workers, 126 spec, 127 ) 128 }) 129 130 BeforeEach(func() { 131 logger = lagertest.NewTestLogger("volume-locality-placement-test") 132 strategy = NewVolumeLocalityPlacementStrategy() 133 134 fakeInput1 := new(workerfakes.FakeInputSource) 135 fakeInput1AS := new(workerfakes.FakeArtifactSource) 136 fakeInput1AS.ExistsOnStub = func(logger lager.Logger, worker Worker) (Volume, bool, error) { 137 switch worker { 138 case compatibleWorkerOneCache1, compatibleWorkerOneCache2, compatibleWorkerTwoCaches: 139 return new(workerfakes.FakeVolume), true, nil 140 default: 141 return nil, false, nil 142 } 143 } 144 fakeInput1.SourceReturns(fakeInput1AS) 145 146 fakeInput2 := new(workerfakes.FakeInputSource) 147 fakeInput2AS := new(workerfakes.FakeArtifactSource) 148 fakeInput2AS.ExistsOnStub = func(logger lager.Logger, worker Worker) (Volume, bool, error) { 149 switch worker { 150 case compatibleWorkerTwoCaches: 151 return new(workerfakes.FakeVolume), true, nil 152 default: 153 return nil, false, nil 154 } 155 } 156 fakeInput2.SourceReturns(fakeInput2AS) 157 158 spec = ContainerSpec{ 159 ImageSpec: ImageSpec{ResourceType: "some-type"}, 160 161 TeamID: 4567, 162 163 Inputs: []InputSource{ 164 fakeInput1, 165 fakeInput2, 166 }, 167 } 168 169 compatibleWorkerOneCache1 = new(workerfakes.FakeWorker) 170 compatibleWorkerOneCache1.SatisfiesReturns(true) 171 172 compatibleWorkerOneCache2 = new(workerfakes.FakeWorker) 173 compatibleWorkerOneCache2.SatisfiesReturns(true) 174 175 compatibleWorkerTwoCaches = new(workerfakes.FakeWorker) 176 compatibleWorkerTwoCaches.SatisfiesReturns(true) 177 178 compatibleWorkerNoCaches1 = new(workerfakes.FakeWorker) 179 compatibleWorkerNoCaches1.SatisfiesReturns(true) 180 181 compatibleWorkerNoCaches2 = new(workerfakes.FakeWorker) 182 compatibleWorkerNoCaches2.SatisfiesReturns(true) 183 }) 184 185 Context("with one having the most local caches", func() { 186 BeforeEach(func() { 187 workers = []Worker{ 188 compatibleWorkerOneCache1, 189 compatibleWorkerTwoCaches, 190 compatibleWorkerNoCaches1, 191 compatibleWorkerNoCaches2, 192 } 193 }) 194 195 It("creates it on the worker with the most caches", func() { 196 Expect(chooseErr).ToNot(HaveOccurred()) 197 Expect(chosenWorker).To(Equal(compatibleWorkerTwoCaches)) 198 }) 199 }) 200 201 Context("with multiple with the same amount of local caches", func() { 202 BeforeEach(func() { 203 workers = []Worker{ 204 compatibleWorkerOneCache1, 205 compatibleWorkerOneCache2, 206 compatibleWorkerNoCaches1, 207 compatibleWorkerNoCaches2, 208 } 209 }) 210 211 It("creates it on a random one of the two", func() { 212 Expect(chooseErr).ToNot(HaveOccurred()) 213 Expect(chosenWorker).To(SatisfyAny(Equal(compatibleWorkerOneCache1), Equal(compatibleWorkerOneCache2))) 214 215 workerChoiceCounts := map[Worker]int{} 216 217 for i := 0; i < 100; i++ { 218 worker, err := strategy.Choose( 219 logger, 220 workers, 221 spec, 222 ) 223 Expect(err).ToNot(HaveOccurred()) 224 Expect(chosenWorker).To(SatisfyAny(Equal(compatibleWorkerOneCache1), Equal(compatibleWorkerOneCache2))) 225 workerChoiceCounts[worker]++ 226 } 227 228 Expect(workerChoiceCounts[compatibleWorkerOneCache1]).ToNot(BeZero()) 229 Expect(workerChoiceCounts[compatibleWorkerOneCache2]).ToNot(BeZero()) 230 Expect(workerChoiceCounts[compatibleWorkerNoCaches1]).To(BeZero()) 231 Expect(workerChoiceCounts[compatibleWorkerNoCaches2]).To(BeZero()) 232 }) 233 }) 234 235 Context("with none having any local caches", func() { 236 BeforeEach(func() { 237 workers = []Worker{ 238 compatibleWorkerNoCaches1, 239 compatibleWorkerNoCaches2, 240 } 241 }) 242 243 It("creates it on a random one of them", func() { 244 Expect(chooseErr).ToNot(HaveOccurred()) 245 Expect(chosenWorker).To(SatisfyAny(Equal(compatibleWorkerNoCaches1), Equal(compatibleWorkerNoCaches2))) 246 247 workerChoiceCounts := map[Worker]int{} 248 249 for i := 0; i < 100; i++ { 250 worker, err := strategy.Choose( 251 logger, 252 workers, 253 spec, 254 ) 255 Expect(err).ToNot(HaveOccurred()) 256 Expect(chosenWorker).To(SatisfyAny(Equal(compatibleWorkerNoCaches1), Equal(compatibleWorkerNoCaches2))) 257 workerChoiceCounts[worker]++ 258 } 259 260 Expect(workerChoiceCounts[compatibleWorkerNoCaches1]).ToNot(BeZero()) 261 Expect(workerChoiceCounts[compatibleWorkerNoCaches2]).ToNot(BeZero()) 262 }) 263 }) 264 }) 265}) 266 267var _ = Describe("RandomPlacementStrategy", func() { 268 Describe("Choose", func() { 269 JustBeforeEach(func() { 270 chosenWorker, chooseErr = strategy.Choose( 271 logger, 272 workers, 273 spec, 274 ) 275 }) 276 277 BeforeEach(func() { 278 strategy = NewRandomPlacementStrategy() 279 280 workers = []Worker{ 281 compatibleWorkerNoCaches1, 282 compatibleWorkerNoCaches2, 283 } 284 }) 285 286 It("creates it on a random one of them", func() { 287 Expect(chooseErr).ToNot(HaveOccurred()) 288 Expect(chosenWorker).To(SatisfyAny(Equal(compatibleWorkerNoCaches1), Equal(compatibleWorkerNoCaches2))) 289 290 workerChoiceCounts := map[Worker]int{} 291 292 for i := 0; i < 100; i++ { 293 worker, err := strategy.Choose( 294 logger, 295 workers, 296 spec, 297 ) 298 Expect(err).ToNot(HaveOccurred()) 299 Expect(chosenWorker).To(SatisfyAny(Equal(compatibleWorkerNoCaches1), Equal(compatibleWorkerNoCaches2))) 300 workerChoiceCounts[worker]++ 301 } 302 303 Expect(workerChoiceCounts[compatibleWorkerNoCaches1]).ToNot(BeZero()) 304 Expect(workerChoiceCounts[compatibleWorkerNoCaches2]).ToNot(BeZero()) 305 }) 306 }) 307}) 308 309var _ = Describe("LimitActiveTasksPlacementStrategy", func() { 310 Describe("Choose", func() { 311 var compatibleWorker1 *workerfakes.FakeWorker 312 var compatibleWorker2 *workerfakes.FakeWorker 313 var compatibleWorker3 *workerfakes.FakeWorker 314 315 BeforeEach(func() { 316 logger = lagertest.NewTestLogger("active-tasks-equal-placement-test") 317 strategy = NewLimitActiveTasksPlacementStrategy(0) 318 compatibleWorker1 = new(workerfakes.FakeWorker) 319 compatibleWorker2 = new(workerfakes.FakeWorker) 320 compatibleWorker3 = new(workerfakes.FakeWorker) 321 322 spec = ContainerSpec{ 323 ImageSpec: ImageSpec{ResourceType: "some-type"}, 324 325 Type: "task", 326 327 TeamID: 4567, 328 329 Inputs: []InputSource{}, 330 } 331 }) 332 333 Context("when there is only one worker with any amount of running tasks", func() { 334 BeforeEach(func() { 335 workers = []Worker{compatibleWorker1} 336 compatibleWorker1.ActiveTasksReturns(42, nil) 337 }) 338 339 It("picks that worker", func() { 340 chosenWorker, chooseErr = strategy.Choose( 341 logger, 342 workers, 343 spec, 344 ) 345 Expect(chooseErr).ToNot(HaveOccurred()) 346 Expect(chosenWorker).To(Equal(compatibleWorker1)) 347 }) 348 }) 349 350 Context("when there are multiple workers", func() { 351 BeforeEach(func() { 352 workers = []Worker{compatibleWorker1, compatibleWorker2, compatibleWorker3} 353 354 compatibleWorker1.ActiveTasksReturns(2, nil) 355 compatibleWorker2.ActiveTasksReturns(1, nil) 356 compatibleWorker3.ActiveTasksReturns(2, nil) 357 }) 358 359 It("a task picks the one with least amount of active tasks", func() { 360 Consistently(func() Worker { 361 chosenWorker, chooseErr = strategy.Choose( 362 logger, 363 workers, 364 spec, 365 ) 366 Expect(chooseErr).ToNot(HaveOccurred()) 367 return chosenWorker 368 }).Should(Equal(compatibleWorker2)) 369 }) 370 371 Context("when all the workers have the same number of active tasks", func() { 372 BeforeEach(func() { 373 workers = []Worker{compatibleWorker1, compatibleWorker2, compatibleWorker3} 374 compatibleWorker1.ActiveTasksReturns(1, nil) 375 compatibleWorker3.ActiveTasksReturns(1, nil) 376 }) 377 378 It("a task picks any of them", func() { 379 Consistently(func() Worker { 380 chosenWorker, chooseErr = strategy.Choose( 381 logger, 382 workers, 383 spec, 384 ) 385 Expect(chooseErr).ToNot(HaveOccurred()) 386 return chosenWorker 387 }).Should(Or(Equal(compatibleWorker1), Equal(compatibleWorker3))) 388 }) 389 }) 390 }) 391 Context("when max-tasks-per-worker is set to 1", func() { 392 BeforeEach(func() { 393 strategy = NewLimitActiveTasksPlacementStrategy(1) 394 }) 395 Context("when there are multiple workers", func() { 396 BeforeEach(func() { 397 workers = []Worker{compatibleWorker1, compatibleWorker2, compatibleWorker3} 398 399 compatibleWorker1.ActiveTasksReturns(1, nil) 400 compatibleWorker2.ActiveTasksReturns(0, nil) 401 compatibleWorker3.ActiveTasksReturns(1, nil) 402 }) 403 404 It("picks the worker with no active tasks", func() { 405 chosenWorker, chooseErr = strategy.Choose( 406 logger, 407 workers, 408 spec, 409 ) 410 Expect(chooseErr).ToNot(HaveOccurred()) 411 Expect(chosenWorker).To(Equal(compatibleWorker2)) 412 }) 413 }) 414 415 Context("when all workers have active tasks", func() { 416 BeforeEach(func() { 417 workers = []Worker{compatibleWorker1, compatibleWorker2, compatibleWorker3} 418 419 compatibleWorker1.ActiveTasksReturns(1, nil) 420 compatibleWorker2.ActiveTasksReturns(1, nil) 421 compatibleWorker3.ActiveTasksReturns(1, nil) 422 }) 423 424 It("picks no worker", func() { 425 chosenWorker, chooseErr = strategy.Choose( 426 logger, 427 workers, 428 spec, 429 ) 430 Expect(chooseErr).ToNot(HaveOccurred()) 431 Expect(chosenWorker).To(BeNil()) 432 }) 433 Context("when the container is not of type 'task'", func() { 434 BeforeEach(func() { 435 spec.Type = "" 436 }) 437 It("picks any worker", func() { 438 Consistently(func() Worker { 439 chosenWorker, chooseErr = strategy.Choose( 440 logger, 441 workers, 442 spec, 443 ) 444 Expect(chooseErr).ToNot(HaveOccurred()) 445 return chosenWorker 446 }).Should(Or(Equal(compatibleWorker1), Equal(compatibleWorker3))) 447 }) 448 }) 449 }) 450 }) 451 }) 452}) 453