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  })