github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/pool_test.go (about) 1 package worker_test 2 3 import ( 4 "code.cloudfoundry.org/lager" 5 "code.cloudfoundry.org/lager/lagertest" 6 7 "context" 8 "errors" 9 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 12 . "github.com/pf-qiu/concourse/v6/atc/worker" 13 "github.com/pf-qiu/concourse/v6/atc/worker/workerfakes" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 var _ = Describe("Pool", func() { 19 var ( 20 logger *lagertest.TestLogger 21 pool Pool 22 fakeProvider *workerfakes.FakeWorkerProvider 23 ) 24 25 BeforeEach(func() { 26 logger = lagertest.NewTestLogger("test") 27 fakeProvider = new(workerfakes.FakeWorkerProvider) 28 29 pool = NewPool(fakeProvider) 30 }) 31 32 Describe("FindOrChooseWorkerForContainer", func() { 33 var ( 34 spec ContainerSpec 35 workerSpec WorkerSpec 36 fakeOwner *dbfakes.FakeContainerOwner 37 38 chosenWorker Worker 39 chooseErr error 40 41 incompatibleWorker *workerfakes.FakeWorker 42 compatibleWorker *workerfakes.FakeWorker 43 fakeStrategy *workerfakes.FakeContainerPlacementStrategy 44 ) 45 46 BeforeEach(func() { 47 fakeStrategy = new(workerfakes.FakeContainerPlacementStrategy) 48 49 fakeOwner = new(dbfakes.FakeContainerOwner) 50 51 fakeInput1 := new(workerfakes.FakeInputSource) 52 fakeInput1AS := new(workerfakes.FakeArtifactSource) 53 fakeInput1AS.ExistsOnStub = func(logger lager.Logger, worker Worker) (Volume, bool, error) { 54 switch worker { 55 case compatibleWorkerOneCache1, compatibleWorkerOneCache2, compatibleWorkerTwoCaches: 56 return new(workerfakes.FakeVolume), true, nil 57 default: 58 return nil, false, nil 59 } 60 } 61 fakeInput1.SourceReturns(fakeInput1AS) 62 63 fakeInput2 := new(workerfakes.FakeInputSource) 64 fakeInput2AS := new(workerfakes.FakeArtifactSource) 65 fakeInput2AS.ExistsOnStub = func(logger lager.Logger, worker Worker) (Volume, bool, error) { 66 switch worker { 67 case compatibleWorkerTwoCaches: 68 return new(workerfakes.FakeVolume), true, nil 69 default: 70 return nil, false, nil 71 } 72 } 73 fakeInput2.SourceReturns(fakeInput2AS) 74 75 spec = ContainerSpec{ 76 ImageSpec: ImageSpec{ResourceType: "some-type"}, 77 78 TeamID: 4567, 79 80 Inputs: []InputSource{ 81 fakeInput1, 82 fakeInput2, 83 }, 84 } 85 86 workerSpec = WorkerSpec{ 87 ResourceType: "some-type", 88 TeamID: 4567, 89 Tags: atc.Tags{"some-tag"}, 90 } 91 92 incompatibleWorker = new(workerfakes.FakeWorker) 93 incompatibleWorker.SatisfiesReturns(false) 94 95 compatibleWorker = new(workerfakes.FakeWorker) 96 compatibleWorker.SatisfiesReturns(true) 97 }) 98 99 JustBeforeEach(func() { 100 chosenWorker, chooseErr = pool.FindOrChooseWorkerForContainer( 101 context.TODO(), 102 logger, 103 fakeOwner, 104 spec, 105 workerSpec, 106 fakeStrategy, 107 ) 108 }) 109 110 Context("selects a worker in serial", func() { 111 var ( 112 workerA *workerfakes.FakeWorker 113 ) 114 115 BeforeEach(func() { 116 workerA = new(workerfakes.FakeWorker) 117 workerA.NameReturns("workerA") 118 workerA.SatisfiesReturns(true) 119 120 fakeProvider.FindWorkersForContainerByOwnerReturns([]Worker{workerA}, nil) 121 fakeProvider.RunningWorkersReturns([]Worker{workerA}, nil) 122 fakeStrategy.ChooseReturns(workerA, nil) 123 }) 124 125 }) 126 127 Context("when workers are found with the container", func() { 128 var ( 129 workerA *workerfakes.FakeWorker 130 workerB *workerfakes.FakeWorker 131 workerC *workerfakes.FakeWorker 132 ) 133 134 BeforeEach(func() { 135 workerA = new(workerfakes.FakeWorker) 136 workerA.NameReturns("workerA") 137 workerA.SatisfiesReturns(true) 138 workerB = new(workerfakes.FakeWorker) 139 workerB.NameReturns("workerB") 140 workerC = new(workerfakes.FakeWorker) 141 workerC.NameReturns("workerC") 142 143 fakeProvider.FindWorkersForContainerByOwnerReturns([]Worker{workerA, workerB, workerC}, nil) 144 fakeProvider.RunningWorkersReturns([]Worker{workerA, workerB, workerC}, nil) 145 fakeStrategy.ChooseReturns(workerA, nil) 146 }) 147 148 Context("when one of the workers satisfy the spec", func() { 149 BeforeEach(func() { 150 workerA.SatisfiesReturns(true) 151 workerB.SatisfiesReturns(false) 152 workerC.SatisfiesReturns(false) 153 }) 154 155 It("checks that the workers satisfy the given worker spec", func() { 156 Expect(workerA.SatisfiesCallCount()).To(Equal(1)) 157 _, actualSpec := workerA.SatisfiesArgsForCall(0) 158 Expect(actualSpec).To(Equal(workerSpec)) 159 160 Expect(workerB.SatisfiesCallCount()).To(Equal(1)) 161 _, actualSpec = workerB.SatisfiesArgsForCall(0) 162 Expect(actualSpec).To(Equal(workerSpec)) 163 164 Expect(workerC.SatisfiesCallCount()).To(Equal(1)) 165 _, actualSpec = workerC.SatisfiesArgsForCall(0) 166 Expect(actualSpec).To(Equal(workerSpec)) 167 }) 168 169 It("succeeds and returns the compatible worker with the container", func() { 170 Expect(fakeStrategy.ChooseCallCount()).To(Equal(0)) 171 172 Expect(chooseErr).NotTo(HaveOccurred()) 173 Expect(chosenWorker.Name()).To(Equal(workerA.Name())) 174 }) 175 }) 176 177 Context("when multiple workers satisfy the spec", func() { 178 BeforeEach(func() { 179 workerA.SatisfiesReturns(true) 180 workerB.SatisfiesReturns(true) 181 workerC.SatisfiesReturns(false) 182 }) 183 184 It("succeeds and returns the first compatible worker with the container", func() { 185 Expect(fakeStrategy.ChooseCallCount()).To(Equal(0)) 186 187 Expect(chooseErr).NotTo(HaveOccurred()) 188 Expect(chosenWorker.Name()).To(Equal(workerA.Name())) 189 }) 190 }) 191 192 Context("when no workers satisfy the spec", func() { 193 BeforeEach(func() { 194 workerA.SatisfiesReturns(false) 195 workerB.SatisfiesReturns(false) 196 workerC.SatisfiesReturns(false) 197 }) 198 199 It("returns a NoCompatibleWorkersError", func() { 200 Expect(chooseErr).To(Equal(NoCompatibleWorkersError{ 201 Spec: workerSpec, 202 })) 203 }) 204 }) 205 206 Context("when the worker that have the container does not satisfy the spec", func() { 207 BeforeEach(func() { 208 workerA.SatisfiesReturns(true) 209 workerB.SatisfiesReturns(true) 210 workerC.SatisfiesReturns(false) 211 212 fakeProvider.FindWorkersForContainerByOwnerReturns([]Worker{workerC}, nil) 213 }) 214 215 It("chooses a satisfying worker", func() { 216 Expect(fakeStrategy.ChooseCallCount()).To(Equal(1)) 217 218 Expect(chooseErr).NotTo(HaveOccurred()) 219 Expect(chosenWorker.Name()).ToNot(Equal(workerC.Name())) 220 }) 221 }) 222 }) 223 224 Context("when no worker is found with the container", func() { 225 BeforeEach(func() { 226 fakeProvider.FindWorkersForContainerByOwnerReturns(nil, nil) 227 }) 228 229 Context("with multiple workers", func() { 230 var ( 231 workerA *workerfakes.FakeWorker 232 workerB *workerfakes.FakeWorker 233 workerC *workerfakes.FakeWorker 234 ) 235 236 BeforeEach(func() { 237 workerA = new(workerfakes.FakeWorker) 238 workerB = new(workerfakes.FakeWorker) 239 workerC = new(workerfakes.FakeWorker) 240 workerA.NameReturns("workerA") 241 242 workerA.SatisfiesReturns(true) 243 workerB.SatisfiesReturns(true) 244 workerC.SatisfiesReturns(false) 245 246 fakeProvider.RunningWorkersReturns([]Worker{workerA, workerB, workerC}, nil) 247 fakeStrategy.ChooseReturns(workerA, nil) 248 }) 249 250 It("checks that the workers satisfy the given worker spec", func() { 251 Expect(workerA.SatisfiesCallCount()).To(Equal(1)) 252 _, actualSpec := workerA.SatisfiesArgsForCall(0) 253 Expect(actualSpec).To(Equal(workerSpec)) 254 255 Expect(workerB.SatisfiesCallCount()).To(Equal(1)) 256 _, actualSpec = workerB.SatisfiesArgsForCall(0) 257 Expect(actualSpec).To(Equal(workerSpec)) 258 259 Expect(workerC.SatisfiesCallCount()).To(Equal(1)) 260 _, actualSpec = workerC.SatisfiesArgsForCall(0) 261 Expect(actualSpec).To(Equal(workerSpec)) 262 }) 263 264 It("returns all workers satisfying the spec", func() { 265 _, satisfyingWorkers, _ := fakeStrategy.ChooseArgsForCall(0) 266 Expect(satisfyingWorkers).To(ConsistOf(workerA, workerB)) 267 }) 268 269 Context("when no workers satisfy the spec", func() { 270 BeforeEach(func() { 271 workerA.SatisfiesReturns(false) 272 workerB.SatisfiesReturns(false) 273 workerC.SatisfiesReturns(false) 274 }) 275 276 It("returns a NoCompatibleWorkersError", func() { 277 Expect(chooseErr).To(Equal(NoCompatibleWorkersError{ 278 Spec: workerSpec, 279 })) 280 }) 281 }) 282 }) 283 284 Context("when team workers and general workers satisfy the spec", func() { 285 var ( 286 teamWorker1 *workerfakes.FakeWorker 287 teamWorker2 *workerfakes.FakeWorker 288 teamWorker3 *workerfakes.FakeWorker 289 generalWorker *workerfakes.FakeWorker 290 ) 291 292 BeforeEach(func() { 293 teamWorker1 = new(workerfakes.FakeWorker) 294 teamWorker1.SatisfiesReturns(true) 295 teamWorker1.IsOwnedByTeamReturns(true) 296 teamWorker2 = new(workerfakes.FakeWorker) 297 teamWorker2.SatisfiesReturns(true) 298 teamWorker2.IsOwnedByTeamReturns(true) 299 teamWorker3 = new(workerfakes.FakeWorker) 300 teamWorker3.SatisfiesReturns(false) 301 generalWorker = new(workerfakes.FakeWorker) 302 generalWorker.SatisfiesReturns(true) 303 generalWorker.IsOwnedByTeamReturns(false) 304 fakeProvider.RunningWorkersReturns([]Worker{generalWorker, teamWorker1, teamWorker2, teamWorker3}, nil) 305 fakeStrategy.ChooseReturns(teamWorker1, nil) 306 }) 307 308 It("returns only the team workers that satisfy the spec", func() { 309 _, satisfyingWorkers, _ := fakeStrategy.ChooseArgsForCall(0) 310 Expect(satisfyingWorkers).To(ConsistOf(teamWorker1, teamWorker2)) 311 }) 312 }) 313 314 Context("when only general workers satisfy the spec", func() { 315 var ( 316 teamWorker *workerfakes.FakeWorker 317 generalWorker1 *workerfakes.FakeWorker 318 generalWorker2 *workerfakes.FakeWorker 319 ) 320 321 BeforeEach(func() { 322 teamWorker = new(workerfakes.FakeWorker) 323 teamWorker.SatisfiesReturns(false) 324 generalWorker1 = new(workerfakes.FakeWorker) 325 generalWorker1.SatisfiesReturns(true) 326 generalWorker1.IsOwnedByTeamReturns(false) 327 generalWorker2 = new(workerfakes.FakeWorker) 328 generalWorker2.SatisfiesReturns(false) 329 fakeProvider.RunningWorkersReturns([]Worker{generalWorker1, generalWorker2, teamWorker}, nil) 330 fakeStrategy.ChooseReturns(generalWorker1, nil) 331 }) 332 333 It("returns the general workers that satisfy the spec", func() { 334 _, satisfyingWorkers, _ := fakeStrategy.ChooseArgsForCall(0) 335 Expect(satisfyingWorkers).To(ConsistOf(generalWorker1)) 336 }) 337 }) 338 339 Context("with no workers", func() { 340 BeforeEach(func() { 341 fakeProvider.RunningWorkersReturns([]Worker{}, nil) 342 }) 343 344 It("returns ErrNoWorkers", func() { 345 Expect(chooseErr).To(Equal(ErrNoWorkers)) 346 }) 347 }) 348 349 Context("when getting the workers fails", func() { 350 disaster := errors.New("nope") 351 352 BeforeEach(func() { 353 fakeProvider.RunningWorkersReturns(nil, disaster) 354 }) 355 356 It("returns the error", func() { 357 Expect(chooseErr).To(Equal(disaster)) 358 }) 359 }) 360 361 Context("with no workers available", func() { 362 BeforeEach(func() { 363 fakeProvider.RunningWorkersReturns([]Worker{}, nil) 364 }) 365 366 It("returns ErrNoWorkers", func() { 367 Expect(chooseErr).To(Equal(ErrNoWorkers)) 368 }) 369 }) 370 371 Context("with no compatible workers available", func() { 372 BeforeEach(func() { 373 fakeProvider.RunningWorkersReturns([]Worker{incompatibleWorker}, nil) 374 }) 375 376 It("returns NoCompatibleWorkersError", func() { 377 Expect(chooseErr).To(Equal(NoCompatibleWorkersError{ 378 Spec: workerSpec, 379 })) 380 }) 381 }) 382 383 Context("with compatible workers available", func() { 384 BeforeEach(func() { 385 fakeProvider.RunningWorkersReturns([]Worker{ 386 incompatibleWorker, 387 compatibleWorker, 388 }, nil) 389 }) 390 391 Context("when strategy returns a worker", func() { 392 BeforeEach(func() { 393 fakeStrategy.ChooseReturns(compatibleWorker, nil) 394 }) 395 396 It("chooses a worker", func() { 397 Expect(chooseErr).ToNot(HaveOccurred()) 398 Expect(fakeStrategy.ChooseCallCount()).To(Equal(1)) 399 Expect(chosenWorker.Name()).To(Equal(compatibleWorker.Name())) 400 }) 401 }) 402 403 Context("when strategy errors", func() { 404 var ( 405 strategyError error 406 ) 407 408 BeforeEach(func() { 409 strategyError = errors.New("strategical explosion") 410 fakeStrategy.ChooseReturns(nil, strategyError) 411 }) 412 413 It("returns an error", func() { 414 Expect(chooseErr).To(Equal(strategyError)) 415 }) 416 }) 417 }) 418 }) 419 }) 420 421 })