github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/db_worker_provider_test.go (about)

     1  package worker_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  
    10  	"code.cloudfoundry.org/garden/client"
    11  	"code.cloudfoundry.org/garden/client/connection"
    12  	gfakes "code.cloudfoundry.org/garden/gardenfakes"
    13  	"code.cloudfoundry.org/garden/server"
    14  	"code.cloudfoundry.org/lager/lagertest"
    15  	"github.com/concourse/baggageclaim"
    16  	"github.com/pf-qiu/concourse/v6/atc"
    17  	"github.com/pf-qiu/concourse/v6/atc/db"
    18  	"github.com/pf-qiu/concourse/v6/atc/db/dbfakes"
    19  	"github.com/pf-qiu/concourse/v6/atc/db/lock/lockfakes"
    20  	. "github.com/pf-qiu/concourse/v6/atc/worker"
    21  	"github.com/pf-qiu/concourse/v6/atc/worker/workerfakes"
    22  	"github.com/concourse/retryhttp/retryhttpfakes"
    23  	"github.com/cppforlife/go-semi-semantic/version"
    24  
    25  	"time"
    26  
    27  	. "github.com/onsi/ginkgo"
    28  	. "github.com/onsi/gomega"
    29  	"github.com/onsi/gomega/ghttp"
    30  )
    31  
    32  var _ = Describe("DBProvider", func() {
    33  	var (
    34  		fakeLockFactory *lockfakes.FakeLockFactory
    35  
    36  		logger *lagertest.TestLogger
    37  
    38  		fakeGardenBackend  *gfakes.FakeBackend
    39  		gardenAddr         string
    40  		baggageclaimURL    string
    41  		wantWorkerVersion  version.Version
    42  		baggageclaimServer *ghttp.Server
    43  		gardenServer       *server.GardenServer
    44  		provider           WorkerProvider
    45  
    46  		fakeFetcher                         *workerfakes.FakeFetcher
    47  		fakeImageFactory                    *workerfakes.FakeImageFactory
    48  		fakeDBVolumeRepository              *dbfakes.FakeVolumeRepository
    49  		fakeDBWorkerFactory                 *dbfakes.FakeWorkerFactory
    50  		fakeDBTeamFactory                   *dbfakes.FakeTeamFactory
    51  		fakeDBWorkerBaseResourceTypeFactory *dbfakes.FakeWorkerBaseResourceTypeFactory
    52  		fakeDBWorkerTaskCacheFactory        *dbfakes.FakeWorkerTaskCacheFactory
    53  		fakeDBTaskCacheFactory              *dbfakes.FakeTaskCacheFactory
    54  		fakeDBResourceCacheFactory          *dbfakes.FakeResourceCacheFactory
    55  		fakeDBResourceConfigFactory         *dbfakes.FakeResourceConfigFactory
    56  		fakeCreatingContainer               *dbfakes.FakeCreatingContainer
    57  		fakeCreatedContainer                *dbfakes.FakeCreatedContainer
    58  
    59  		fakeDBTeam *dbfakes.FakeTeam
    60  
    61  		workers    []Worker
    62  		workersErr error
    63  
    64  		fakeWorker1 *dbfakes.FakeWorker
    65  		fakeWorker2 *dbfakes.FakeWorker
    66  	)
    67  
    68  	const (
    69  		baggageclaimResponseHeaderTimeout = 10 * time.Minute
    70  		gardenRequestTimeout              = 5 * time.Minute
    71  	)
    72  
    73  	BeforeEach(func() {
    74  		var err error
    75  
    76  		baggageclaimServer = ghttp.NewServer()
    77  
    78  		baggageclaimServer.RouteToHandler("POST", "/volumes", ghttp.RespondWithJSONEncoded(
    79  			http.StatusCreated,
    80  			baggageclaim.VolumeResponse{Handle: "vol-handle"},
    81  		))
    82  		baggageclaimServer.RouteToHandler("PUT", "/volumes/vol-handle/ttl", ghttp.RespondWith(
    83  			http.StatusNoContent,
    84  			nil,
    85  		))
    86  		baggageclaimServer.RouteToHandler("GET", "/volumes/vol-handle", ghttp.RespondWithJSONEncoded(
    87  			http.StatusOK,
    88  			baggageclaim.VolumeResponse{Handle: "vol-handle"},
    89  		))
    90  		baggageclaimServer.RouteToHandler("GET", "/volumes/certificates", ghttp.RespondWithJSONEncoded(
    91  			http.StatusOK,
    92  			baggageclaim.VolumeResponse{Handle: "certificates", Path: "/resource/certs"},
    93  		))
    94  
    95  		gardenAddr = fmt.Sprintf("0.0.0.0:%d", 8888+GinkgoParallelNode())
    96  		fakeGardenBackend = new(gfakes.FakeBackend)
    97  		logger = lagertest.NewTestLogger("test")
    98  		gardenServer = server.New("tcp", gardenAddr, 0, fakeGardenBackend, logger)
    99  
   100  		go func() {
   101  			defer GinkgoRecover()
   102  			err = gardenServer.ListenAndServe()
   103  			Expect(err).NotTo(HaveOccurred())
   104  
   105  		}()
   106  
   107  		apiClient := client.New(connection.New("tcp", gardenAddr))
   108  		Eventually(apiClient.Ping).Should(Succeed())
   109  
   110  		err = gardenServer.SetupBomberman()
   111  		Expect(err).NotTo(HaveOccurred())
   112  
   113  		worker1Version := "1.2.3"
   114  
   115  		fakeWorker1 = new(dbfakes.FakeWorker)
   116  		fakeWorker1.NameReturns("some-worker")
   117  		fakeWorker1.GardenAddrReturns(&gardenAddr)
   118  		fakeWorker1.BaggageclaimURLReturns(&baggageclaimURL)
   119  		fakeWorker1.StateReturns(db.WorkerStateRunning)
   120  		fakeWorker1.ActiveContainersReturns(2)
   121  		fakeWorker1.ResourceTypesReturns([]atc.WorkerResourceType{
   122  			{Type: "some-resource-a", Image: "some-image-a"}})
   123  
   124  		fakeWorker1.VersionReturns(&worker1Version)
   125  
   126  		worker2Version := "1.2.4"
   127  
   128  		fakeWorker2 = new(dbfakes.FakeWorker)
   129  		fakeWorker2.NameReturns("some-other-worker")
   130  		fakeWorker2.GardenAddrReturns(&gardenAddr)
   131  		fakeWorker2.BaggageclaimURLReturns(&baggageclaimURL)
   132  		fakeWorker2.StateReturns(db.WorkerStateRunning)
   133  		fakeWorker2.ActiveContainersReturns(2)
   134  		fakeWorker2.ResourceTypesReturns([]atc.WorkerResourceType{
   135  			{Type: "some-resource-b", Image: "some-image-b"}})
   136  
   137  		fakeWorker2.VersionReturns(&worker2Version)
   138  
   139  		fakeFetcher = new(workerfakes.FakeFetcher)
   140  		fakeImageFactory = new(workerfakes.FakeImageFactory)
   141  		fakeImage := new(workerfakes.FakeImage)
   142  		fakeImage.FetchForContainerReturns(FetchedImage{}, nil)
   143  		fakeImageFactory.GetImageReturns(fakeImage, nil)
   144  		fakeDBTeamFactory = new(dbfakes.FakeTeamFactory)
   145  		fakeDBTeam = new(dbfakes.FakeTeam)
   146  		fakeDBTeam.IDReturns(1)
   147  		fakeDBTeamFactory.GetByIDReturns(fakeDBTeam)
   148  		fakeDBVolumeRepository = new(dbfakes.FakeVolumeRepository)
   149  
   150  		fakeBackOffFactory := new(retryhttpfakes.FakeBackOffFactory)
   151  		fakeBackOff := new(retryhttpfakes.FakeBackOff)
   152  		fakeBackOffFactory.NewBackOffReturns(fakeBackOff)
   153  		fakeDBResourceCacheFactory = new(dbfakes.FakeResourceCacheFactory)
   154  		fakeDBResourceConfigFactory = new(dbfakes.FakeResourceConfigFactory)
   155  		fakeDBWorkerBaseResourceTypeFactory = new(dbfakes.FakeWorkerBaseResourceTypeFactory)
   156  		fakeDBTaskCacheFactory = new(dbfakes.FakeTaskCacheFactory)
   157  		fakeDBWorkerTaskCacheFactory = new(dbfakes.FakeWorkerTaskCacheFactory)
   158  		fakeLock := new(lockfakes.FakeLock)
   159  
   160  		fakeLockFactory = new(lockfakes.FakeLockFactory)
   161  		fakeLockFactory.AcquireReturns(fakeLock, true, nil)
   162  
   163  		fakeDBWorkerFactory = new(dbfakes.FakeWorkerFactory)
   164  
   165  		wantWorkerVersion, err = version.NewVersionFromString("1.1.0")
   166  		Expect(err).ToNot(HaveOccurred())
   167  
   168  		provider = NewDBWorkerProvider(
   169  			fakeLockFactory,
   170  			fakeBackOffFactory,
   171  			fakeFetcher,
   172  			fakeImageFactory,
   173  			fakeDBResourceCacheFactory,
   174  			fakeDBResourceConfigFactory,
   175  			fakeDBWorkerBaseResourceTypeFactory,
   176  			fakeDBTaskCacheFactory,
   177  			fakeDBWorkerTaskCacheFactory,
   178  			fakeDBVolumeRepository,
   179  			fakeDBTeamFactory,
   180  			fakeDBWorkerFactory,
   181  			wantWorkerVersion,
   182  			baggageclaimResponseHeaderTimeout,
   183  			gardenRequestTimeout,
   184  		)
   185  		baggageclaimURL = baggageclaimServer.URL()
   186  	})
   187  
   188  	AfterEach(func() {
   189  		gardenServer.Stop()
   190  
   191  		Eventually(func() error {
   192  			conn, err := net.Dial("tcp", gardenAddr)
   193  			if err == nil {
   194  				conn.Close()
   195  			}
   196  
   197  			return err
   198  		}).Should(HaveOccurred())
   199  
   200  		baggageclaimServer.Close()
   201  	})
   202  
   203  	Describe("RunningWorkers", func() {
   204  		JustBeforeEach(func() {
   205  			workers, workersErr = provider.RunningWorkers(logger)
   206  		})
   207  
   208  		Context("when the database yields workers", func() {
   209  			BeforeEach(func() {
   210  				fakeDBWorkerFactory.WorkersReturns([]db.Worker{fakeWorker1, fakeWorker2}, nil)
   211  				fakeDBWorkerFactory.BuildContainersCountPerWorkerReturns(map[string]int{
   212  					fakeWorker1.Name(): 57,
   213  					fakeWorker2.Name(): 68,
   214  				}, nil)
   215  			})
   216  
   217  			It("succeeds", func() {
   218  				Expect(workersErr).NotTo(HaveOccurred())
   219  			})
   220  
   221  			It("returns a worker for each one", func() {
   222  				Expect(workers).To(HaveLen(2))
   223  			})
   224  
   225  			It("correctly populates the numBuildContainers field in each worker", func() {
   226  				Expect([]int{workers[0].BuildContainers(), workers[1].BuildContainers()}).To(ConsistOf(57, 68))
   227  			})
   228  
   229  			Context("when some of the workers returned are stalled or landing", func() {
   230  				BeforeEach(func() {
   231  					landingWorker := new(dbfakes.FakeWorker)
   232  					landingWorker.NameReturns("landing-worker")
   233  					landingWorker.GardenAddrReturns(&gardenAddr)
   234  					landingWorker.BaggageclaimURLReturns(&baggageclaimURL)
   235  					landingWorker.StateReturns(db.WorkerStateLanding)
   236  					landingWorker.ActiveContainersReturns(5)
   237  					landingWorker.ResourceTypesReturns([]atc.WorkerResourceType{
   238  						{Type: "some-resource-b", Image: "some-image-b"}})
   239  
   240  					stalledWorker := new(dbfakes.FakeWorker)
   241  					stalledWorker.NameReturns("stalled-worker")
   242  					stalledWorker.GardenAddrReturns(&gardenAddr)
   243  					stalledWorker.BaggageclaimURLReturns(&baggageclaimURL)
   244  					stalledWorker.StateReturns(db.WorkerStateStalled)
   245  					stalledWorker.ActiveContainersReturns(0)
   246  					stalledWorker.ResourceTypesReturns([]atc.WorkerResourceType{
   247  						{Type: "some-resource-b", Image: "some-image-b"}})
   248  
   249  					fakeDBWorkerFactory.WorkersReturns(
   250  						[]db.Worker{
   251  							fakeWorker1,
   252  							stalledWorker,
   253  							landingWorker,
   254  						}, nil)
   255  				})
   256  
   257  				It("only returns workers for the running ones", func() {
   258  					Expect(workers).To(HaveLen(1))
   259  					Expect(workersErr).NotTo(HaveOccurred())
   260  				})
   261  			})
   262  
   263  			Context("when a worker's major version is higher or lower than the atc worker version", func() {
   264  				BeforeEach(func() {
   265  					worker1 := new(dbfakes.FakeWorker)
   266  					worker1.NameReturns("worker-1")
   267  					worker1.GardenAddrReturns(&gardenAddr)
   268  					worker1.BaggageclaimURLReturns(&baggageclaimURL)
   269  					worker1.StateReturns(db.WorkerStateRunning)
   270  					worker1.ActiveContainersReturns(5)
   271  					worker1.ResourceTypesReturns([]atc.WorkerResourceType{
   272  						{Type: "some-resource-b", Image: "some-image-b"}})
   273  					version1 := "1.1.0"
   274  					worker1.VersionReturns(&version1)
   275  
   276  					worker2 := new(dbfakes.FakeWorker)
   277  					worker2.NameReturns("worker-2")
   278  					worker2.GardenAddrReturns(&gardenAddr)
   279  					worker2.BaggageclaimURLReturns(&baggageclaimURL)
   280  					worker2.StateReturns(db.WorkerStateRunning)
   281  					worker2.ActiveContainersReturns(0)
   282  					worker2.ResourceTypesReturns([]atc.WorkerResourceType{
   283  						{Type: "some-resource-b", Image: "some-image-b"}})
   284  					version2 := "2.0.0"
   285  					worker2.VersionReturns(&version2)
   286  
   287  					worker3 := new(dbfakes.FakeWorker)
   288  					worker3.NameReturns("worker-2")
   289  					worker3.GardenAddrReturns(&gardenAddr)
   290  					worker3.BaggageclaimURLReturns(&baggageclaimURL)
   291  					worker3.StateReturns(db.WorkerStateRunning)
   292  					worker3.ActiveContainersReturns(0)
   293  					worker3.ResourceTypesReturns([]atc.WorkerResourceType{
   294  						{Type: "some-resource-b", Image: "some-image-b"}})
   295  					version3 := "0.0.0"
   296  					worker3.VersionReturns(&version3)
   297  
   298  					fakeDBWorkerFactory.WorkersReturns(
   299  						[]db.Worker{
   300  							worker3,
   301  							worker2,
   302  							worker1,
   303  						}, nil)
   304  				})
   305  
   306  				It("only returns workers with same major version", func() {
   307  					Expect(workers).To(HaveLen(1))
   308  					Expect(workers[0].Name()).To(Equal("worker-1"))
   309  					Expect(workersErr).NotTo(HaveOccurred())
   310  				})
   311  			})
   312  
   313  			Context("when a worker's minor version is higher or lower than the atc worker version", func() {
   314  				BeforeEach(func() {
   315  					worker1 := new(dbfakes.FakeWorker)
   316  					worker1.NameReturns("worker-1")
   317  					worker1.GardenAddrReturns(&gardenAddr)
   318  					worker1.BaggageclaimURLReturns(&baggageclaimURL)
   319  					worker1.StateReturns(db.WorkerStateRunning)
   320  					worker1.ActiveContainersReturns(5)
   321  					worker1.ResourceTypesReturns([]atc.WorkerResourceType{
   322  						{Type: "some-resource-b", Image: "some-image-b"}})
   323  					version1 := "1.1.0"
   324  					worker1.VersionReturns(&version1)
   325  
   326  					worker2 := new(dbfakes.FakeWorker)
   327  					worker2.NameReturns("worker-2")
   328  					worker2.GardenAddrReturns(&gardenAddr)
   329  					worker2.BaggageclaimURLReturns(&baggageclaimURL)
   330  					worker2.StateReturns(db.WorkerStateRunning)
   331  					worker2.ActiveContainersReturns(0)
   332  					worker2.ResourceTypesReturns([]atc.WorkerResourceType{
   333  						{Type: "some-resource-b", Image: "some-image-b"}})
   334  					version2 := "1.2.0"
   335  					worker2.VersionReturns(&version2)
   336  
   337  					worker3 := new(dbfakes.FakeWorker)
   338  					worker3.NameReturns("worker-2")
   339  					worker3.GardenAddrReturns(&gardenAddr)
   340  					worker3.BaggageclaimURLReturns(&baggageclaimURL)
   341  					worker3.StateReturns(db.WorkerStateRunning)
   342  					worker3.ActiveContainersReturns(0)
   343  					worker3.ResourceTypesReturns([]atc.WorkerResourceType{
   344  						{Type: "some-resource-b", Image: "some-image-b"}})
   345  					version3 := "1.0.0"
   346  					worker3.VersionReturns(&version3)
   347  
   348  					fakeDBWorkerFactory.WorkersReturns(
   349  						[]db.Worker{
   350  							worker3,
   351  							worker2,
   352  							worker1,
   353  						}, nil)
   354  				})
   355  
   356  				It("only returns workers with same or higher minor version", func() {
   357  					Expect(workers).To(HaveLen(2))
   358  					Expect(workers[1].Name()).To(Equal("worker-1"))
   359  					Expect(workers[0].Name()).To(Equal("worker-2"))
   360  					Expect(workersErr).NotTo(HaveOccurred())
   361  				})
   362  			})
   363  
   364  			Context("when a worker does not have a version (outdated)", func() {
   365  				BeforeEach(func() {
   366  					worker1 := new(dbfakes.FakeWorker)
   367  					worker1.NameReturns("worker-1")
   368  					worker1.GardenAddrReturns(&gardenAddr)
   369  					worker1.BaggageclaimURLReturns(&baggageclaimURL)
   370  					worker1.StateReturns(db.WorkerStateRunning)
   371  					worker1.ActiveContainersReturns(5)
   372  					worker1.ResourceTypesReturns([]atc.WorkerResourceType{
   373  						{Type: "some-resource-b", Image: "some-image-b"}})
   374  
   375  					fakeDBWorkerFactory.WorkersReturns(
   376  						[]db.Worker{
   377  							worker1,
   378  						}, nil)
   379  				})
   380  
   381  				It("does not return the worker", func() {
   382  					Expect(workers).To(BeEmpty())
   383  					Expect(workersErr).NotTo(HaveOccurred())
   384  				})
   385  			})
   386  
   387  			Context("when a worker's version is incorretly formatted", func() {
   388  				BeforeEach(func() {
   389  					worker1 := new(dbfakes.FakeWorker)
   390  					worker1.NameReturns("worker-1")
   391  					worker1.GardenAddrReturns(&gardenAddr)
   392  					worker1.BaggageclaimURLReturns(&baggageclaimURL)
   393  					worker1.StateReturns(db.WorkerStateRunning)
   394  					worker1.ActiveContainersReturns(5)
   395  					worker1.ResourceTypesReturns([]atc.WorkerResourceType{
   396  						{Type: "some-resource-b", Image: "some-image-b"}})
   397  					version1 := "1.1..0.2-bogus=version"
   398  					worker1.VersionReturns(&version1)
   399  
   400  					fakeDBWorkerFactory.WorkersReturns(
   401  						[]db.Worker{
   402  							worker1,
   403  						}, nil)
   404  				})
   405  
   406  				It("does not return the worker", func() {
   407  					Expect(workers).To(BeEmpty())
   408  					Expect(workersErr).NotTo(HaveOccurred())
   409  				})
   410  			})
   411  
   412  			Context("creating the connection to garden", func() {
   413  				var (
   414  					containerSpec ContainerSpec
   415  				)
   416  
   417  				JustBeforeEach(func() {
   418  					containerSpec = ContainerSpec{
   419  						ImageSpec: ImageSpec{
   420  							ResourceType: "some-resource-a",
   421  						},
   422  					}
   423  
   424  					fakeContainer := new(gfakes.FakeContainer)
   425  					fakeContainer.HandleReturns("created-handle")
   426  
   427  					fakeGardenBackend.CreateReturns(fakeContainer, nil)
   428  					fakeGardenBackend.LookupReturns(fakeContainer, nil)
   429  
   430  					By("connecting to the worker")
   431  					fakeDBWorkerFactory.GetWorkerReturns(fakeWorker1, true, nil)
   432  					container, err := workers[0].FindOrCreateContainer(
   433  						context.TODO(),
   434  						logger,
   435  						db.NewBuildStepContainerOwner(42, atc.PlanID("some-plan-id"), 1),
   436  						db.ContainerMetadata{},
   437  						containerSpec,
   438  					)
   439  					Expect(err).NotTo(HaveOccurred())
   440  
   441  					err = container.Destroy()
   442  					Expect(err).NotTo(HaveOccurred())
   443  
   444  					By("restarting the worker with a new address")
   445  					gardenServer.Stop()
   446  
   447  					Eventually(func() error {
   448  						conn, err := net.Dial("tcp", gardenAddr)
   449  						if err == nil {
   450  							conn.Close()
   451  						}
   452  
   453  						return err
   454  					}).Should(HaveOccurred())
   455  
   456  					gardenAddr = fmt.Sprintf("0.0.0.0:%d", 7777+GinkgoParallelNode())
   457  
   458  					gardenServer = server.New("tcp", gardenAddr, 0, fakeGardenBackend, logger)
   459  					err = gardenServer.Start()
   460  					Expect(err).NotTo(HaveOccurred())
   461  				})
   462  			})
   463  
   464  			Describe("a created container", func() {
   465  				BeforeEach(func() {
   466  					createdVolume := new(dbfakes.FakeCreatedVolume)
   467  					createdVolume.HandleReturns("vol-handle")
   468  					fakeDBWorkerFactory.GetWorkerReturns(fakeWorker1, true, nil)
   469  					fakeDBVolumeRepository.FindContainerVolumeReturns(nil, createdVolume, nil)
   470  					fakeDBVolumeRepository.FindBaseResourceTypeVolumeReturns(nil, createdVolume, nil)
   471  
   472  					fakeCreatingContainer = new(dbfakes.FakeCreatingContainer)
   473  					fakeCreatingContainer.HandleReturns("some-handle")
   474  					fakeCreatedContainer = new(dbfakes.FakeCreatedContainer)
   475  					fakeCreatingContainer.CreatedReturns(fakeCreatedContainer, nil)
   476  					fakeWorker1.CreateContainerReturns(fakeCreatingContainer, nil)
   477  					fakeWorker1.FindContainerReturns(fakeCreatingContainer, nil, nil)
   478  
   479  					workerBaseResourceType := &db.UsedWorkerBaseResourceType{ID: 42}
   480  					fakeDBWorkerBaseResourceTypeFactory.FindReturns(workerBaseResourceType, true, nil)
   481  				})
   482  
   483  				It("calls through to garden", func() {
   484  					containerSpec := ContainerSpec{
   485  						ImageSpec: ImageSpec{
   486  							ResourceType: "some-resource-a",
   487  						},
   488  					}
   489  
   490  					fakeContainer := new(gfakes.FakeContainer)
   491  					fakeContainer.HandleReturns("created-handle")
   492  
   493  					fakeGardenBackend.CreateReturns(fakeContainer, nil)
   494  					fakeGardenBackend.LookupReturns(fakeContainer, nil)
   495  
   496  					container, err := workers[0].FindOrCreateContainer(
   497  						context.TODO(),
   498  						logger,
   499  						db.NewBuildStepContainerOwner(42, atc.PlanID("some-plan-id"), 1),
   500  						db.ContainerMetadata{},
   501  						containerSpec,
   502  					)
   503  					Expect(err).NotTo(HaveOccurred())
   504  
   505  					Expect(container.Handle()).To(Equal("created-handle"))
   506  
   507  					Expect(fakeGardenBackend.CreateCallCount()).To(Equal(1))
   508  
   509  					err = container.Destroy()
   510  					Expect(err).NotTo(HaveOccurred())
   511  
   512  					Expect(fakeGardenBackend.DestroyCallCount()).To(Equal(1))
   513  					Expect(fakeGardenBackend.DestroyArgsForCall(0)).To(Equal("created-handle"))
   514  				})
   515  			})
   516  		})
   517  
   518  		Context("when the database fails to return workers", func() {
   519  			disaster := errors.New("nope")
   520  
   521  			BeforeEach(func() {
   522  				fakeDBWorkerFactory.WorkersReturns(nil, disaster)
   523  			})
   524  
   525  			It("returns the error", func() {
   526  				Expect(workersErr).To(Equal(disaster))
   527  			})
   528  		})
   529  	})
   530  
   531  	Describe("FindWorkerForContainer", func() {
   532  		var (
   533  			foundWorker Worker
   534  			found       bool
   535  			findErr     error
   536  		)
   537  
   538  		JustBeforeEach(func() {
   539  			foundWorker, found, findErr = provider.FindWorkerForContainer(
   540  				logger,
   541  				345278,
   542  				"some-handle",
   543  			)
   544  		})
   545  
   546  		Context("when the worker is found", func() {
   547  			var fakeExistingWorker *dbfakes.FakeWorker
   548  
   549  			BeforeEach(func() {
   550  				addr := "1.2.3.4:7777"
   551  
   552  				fakeExistingWorker = new(dbfakes.FakeWorker)
   553  				fakeExistingWorker.NameReturns("some-worker")
   554  				fakeExistingWorker.GardenAddrReturns(&addr)
   555  				workerVersion := "1.1.0"
   556  				fakeExistingWorker.VersionReturns(&workerVersion)
   557  
   558  				fakeDBTeam.FindWorkerForContainerReturns(fakeExistingWorker, true, nil)
   559  			})
   560  
   561  			It("returns true", func() {
   562  				Expect(found).To(BeTrue())
   563  				Expect(findErr).ToNot(HaveOccurred())
   564  			})
   565  
   566  			It("returns the worker", func() {
   567  				Expect(foundWorker).ToNot(BeNil())
   568  				Expect(foundWorker.Name()).To(Equal("some-worker"))
   569  			})
   570  
   571  			It("found the worker for the right handle", func() {
   572  				handle := fakeDBTeam.FindWorkerForContainerArgsForCall(0)
   573  				Expect(handle).To(Equal("some-handle"))
   574  			})
   575  
   576  			It("found the right team", func() {
   577  				actualTeam := fakeDBTeamFactory.GetByIDArgsForCall(0)
   578  				Expect(actualTeam).To(Equal(345278))
   579  			})
   580  
   581  			Context("when the worker version is outdated", func() {
   582  				BeforeEach(func() {
   583  					fakeExistingWorker.VersionReturns(nil)
   584  				})
   585  
   586  				It("returns an error", func() {
   587  					Expect(findErr).ToNot(HaveOccurred())
   588  					Expect(foundWorker).To(BeNil())
   589  					Expect(found).To(BeFalse())
   590  				})
   591  			})
   592  		})
   593  
   594  		Context("when the worker is not found", func() {
   595  			BeforeEach(func() {
   596  				fakeDBTeam.FindWorkerForContainerReturns(nil, false, nil)
   597  			})
   598  
   599  			It("returns false", func() {
   600  				Expect(findErr).ToNot(HaveOccurred())
   601  				Expect(foundWorker).To(BeNil())
   602  				Expect(found).To(BeFalse())
   603  			})
   604  		})
   605  
   606  		Context("when finding the worker fails", func() {
   607  			disaster := errors.New("nope")
   608  
   609  			BeforeEach(func() {
   610  				fakeDBTeam.FindWorkerForContainerReturns(nil, false, disaster)
   611  			})
   612  
   613  			It("returns the error", func() {
   614  				Expect(findErr).To(Equal(disaster))
   615  				Expect(foundWorker).To(BeNil())
   616  				Expect(found).To(BeFalse())
   617  			})
   618  		})
   619  	})
   620  
   621  	Describe("FindWorkerForVolume", func() {
   622  		var (
   623  			foundWorker Worker
   624  			found       bool
   625  			findErr     error
   626  		)
   627  
   628  		JustBeforeEach(func() {
   629  			foundWorker, found, findErr = provider.FindWorkerForVolume(
   630  				logger,
   631  				345278,
   632  				"some-handle",
   633  			)
   634  		})
   635  
   636  		Context("when the worker is found", func() {
   637  			var fakeExistingWorker *dbfakes.FakeWorker
   638  
   639  			BeforeEach(func() {
   640  				addr := "1.2.3.4:7777"
   641  
   642  				fakeExistingWorker = new(dbfakes.FakeWorker)
   643  				fakeExistingWorker.NameReturns("some-worker")
   644  				fakeExistingWorker.GardenAddrReturns(&addr)
   645  				workerVersion := "1.1.0"
   646  				fakeExistingWorker.VersionReturns(&workerVersion)
   647  
   648  				fakeDBTeam.FindWorkerForVolumeReturns(fakeExistingWorker, true, nil)
   649  			})
   650  
   651  			It("returns true", func() {
   652  				Expect(found).To(BeTrue())
   653  				Expect(findErr).ToNot(HaveOccurred())
   654  			})
   655  
   656  			It("returns the worker", func() {
   657  				Expect(foundWorker).ToNot(BeNil())
   658  				Expect(foundWorker.Name()).To(Equal("some-worker"))
   659  			})
   660  
   661  			It("found the worker for the right handle", func() {
   662  				handle := fakeDBTeam.FindWorkerForVolumeArgsForCall(0)
   663  				Expect(handle).To(Equal("some-handle"))
   664  			})
   665  
   666  			It("found the right team", func() {
   667  				actualTeam := fakeDBTeamFactory.GetByIDArgsForCall(0)
   668  				Expect(actualTeam).To(Equal(345278))
   669  			})
   670  
   671  			Context("when the worker version is outdated", func() {
   672  				BeforeEach(func() {
   673  					fakeExistingWorker.VersionReturns(nil)
   674  				})
   675  
   676  				It("returns an error", func() {
   677  					Expect(findErr).ToNot(HaveOccurred())
   678  					Expect(foundWorker).To(BeNil())
   679  					Expect(found).To(BeFalse())
   680  				})
   681  			})
   682  		})
   683  
   684  		Context("when the worker is not found", func() {
   685  			BeforeEach(func() {
   686  				fakeDBTeam.FindWorkerForVolumeReturns(nil, false, nil)
   687  			})
   688  
   689  			It("returns false", func() {
   690  				Expect(findErr).ToNot(HaveOccurred())
   691  				Expect(foundWorker).To(BeNil())
   692  				Expect(found).To(BeFalse())
   693  			})
   694  		})
   695  
   696  		Context("when finding the worker fails", func() {
   697  			disaster := errors.New("nope")
   698  
   699  			BeforeEach(func() {
   700  				fakeDBTeam.FindWorkerForVolumeReturns(nil, false, disaster)
   701  			})
   702  
   703  			It("returns the error", func() {
   704  				Expect(findErr).To(Equal(disaster))
   705  				Expect(foundWorker).To(BeNil())
   706  				Expect(found).To(BeFalse())
   707  			})
   708  		})
   709  	})
   710  
   711  	Describe("FindWorkersForContainerByOwner", func() {
   712  		var (
   713  			fakeOwner *dbfakes.FakeContainerOwner
   714  
   715  			foundWorkers []Worker
   716  			findErr      error
   717  		)
   718  
   719  		BeforeEach(func() {
   720  			fakeOwner = new(dbfakes.FakeContainerOwner)
   721  		})
   722  
   723  		JustBeforeEach(func() {
   724  			foundWorkers, findErr = provider.FindWorkersForContainerByOwner(
   725  				logger,
   726  				fakeOwner,
   727  			)
   728  		})
   729  
   730  		Context("when there is a worker", func() {
   731  			var fakeExistingWorker *dbfakes.FakeWorker
   732  
   733  			BeforeEach(func() {
   734  				addr := "1.2.3.4:7777"
   735  
   736  				fakeExistingWorker = new(dbfakes.FakeWorker)
   737  				fakeExistingWorker.NameReturns("some-worker")
   738  				fakeExistingWorker.GardenAddrReturns(&addr)
   739  				workerVersion := "1.1.0"
   740  				fakeExistingWorker.VersionReturns(&workerVersion)
   741  
   742  				fakeDBWorkerFactory.FindWorkersForContainerByOwnerReturns([]db.Worker{fakeExistingWorker}, nil)
   743  			})
   744  
   745  			It("finds the worker", func() {
   746  				Expect(foundWorkers).To(HaveLen(1))
   747  				Expect(foundWorkers[0].Name()).To(Equal("some-worker"))
   748  				Expect(findErr).ToNot(HaveOccurred())
   749  			})
   750  
   751  			It("found the worker for the right owner", func() {
   752  				owner := fakeDBWorkerFactory.FindWorkersForContainerByOwnerArgsForCall(0)
   753  				Expect(owner).To(Equal(fakeOwner))
   754  			})
   755  
   756  			Context("when the worker version is outdated", func() {
   757  				BeforeEach(func() {
   758  					fakeExistingWorker.VersionReturns(nil)
   759  				})
   760  
   761  				It("returns an error", func() {
   762  					Expect(findErr).ToNot(HaveOccurred())
   763  					Expect(foundWorkers).To(BeNil())
   764  				})
   765  			})
   766  		})
   767  
   768  		Context("when there are multiple workers", func() {
   769  			var fakeExistingWorker *dbfakes.FakeWorker
   770  			var fakeExistingWorker2 *dbfakes.FakeWorker
   771  			var fakeExistingWorker3 *dbfakes.FakeWorker
   772  
   773  			BeforeEach(func() {
   774  				addr := "1.2.3.4:7777"
   775  
   776  				fakeExistingWorker = new(dbfakes.FakeWorker)
   777  				fakeExistingWorker.NameReturns("some-worker")
   778  				fakeExistingWorker.GardenAddrReturns(&addr)
   779  				workerVersion := "1.1.0"
   780  				fakeExistingWorker.VersionReturns(&workerVersion)
   781  
   782  				addr2 := "1.2.3.5:7777"
   783  				fakeExistingWorker2 = new(dbfakes.FakeWorker)
   784  				fakeExistingWorker2.NameReturns("some-worker-2")
   785  				fakeExistingWorker2.GardenAddrReturns(&addr2)
   786  				fakeExistingWorker2.VersionReturns(&workerVersion)
   787  
   788  				addr3 := "1.2.3.6:7777"
   789  				fakeExistingWorker3 = new(dbfakes.FakeWorker)
   790  				fakeExistingWorker3.NameReturns("some-worker-3")
   791  				fakeExistingWorker3.GardenAddrReturns(&addr3)
   792  				fakeExistingWorker3.VersionReturns(&workerVersion)
   793  
   794  				fakeDBWorkerFactory.FindWorkersForContainerByOwnerReturns([]db.Worker{fakeExistingWorker, fakeExistingWorker2, fakeExistingWorker3}, nil)
   795  			})
   796  
   797  			It("finds both the worker", func() {
   798  				Expect(foundWorkers).To(HaveLen(3))
   799  
   800  				workerNames := []string{}
   801  				for _, w := range foundWorkers {
   802  					workerNames = append(workerNames, w.Name())
   803  				}
   804  
   805  				Expect(workerNames).To(ConsistOf([]string{"some-worker", "some-worker-2", "some-worker-3"}))
   806  				Expect(findErr).ToNot(HaveOccurred())
   807  			})
   808  
   809  			Context("when one of the worker version is outdated", func() {
   810  				BeforeEach(func() {
   811  					workerVersionOld := "1.0.0"
   812  					fakeExistingWorker3.VersionReturns(&workerVersionOld)
   813  				})
   814  
   815  				It("returns the other two workers", func() {
   816  					Expect(findErr).ToNot(HaveOccurred())
   817  					Expect(foundWorkers).To(HaveLen(2))
   818  
   819  					workerNames := []string{}
   820  					for _, w := range foundWorkers {
   821  						workerNames = append(workerNames, w.Name())
   822  					}
   823  
   824  					Expect(workerNames).To(ConsistOf([]string{"some-worker", "some-worker-2"}))
   825  				})
   826  			})
   827  		})
   828  
   829  		Context("when the worker is not found", func() {
   830  			BeforeEach(func() {
   831  				fakeDBWorkerFactory.FindWorkersForContainerByOwnerReturns([]db.Worker{}, nil)
   832  			})
   833  
   834  			It("returns empty list of workers", func() {
   835  				Expect(findErr).ToNot(HaveOccurred())
   836  				Expect(foundWorkers).To(BeNil())
   837  			})
   838  		})
   839  
   840  		Context("when finding the worker fails", func() {
   841  			disaster := errors.New("nope")
   842  
   843  			BeforeEach(func() {
   844  				fakeDBWorkerFactory.FindWorkersForContainerByOwnerReturns(nil, disaster)
   845  			})
   846  
   847  			It("returns the error", func() {
   848  				Expect(findErr).To(Equal(disaster))
   849  				Expect(foundWorkers).To(BeNil())
   850  			})
   851  		})
   852  	})
   853  })