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

     1  package db_test
     2  
     3  import (
     4  	"time"
     5  
     6  	sq "github.com/Masterminds/squirrel"
     7  	"github.com/pf-qiu/concourse/v6/atc"
     8  	"github.com/pf-qiu/concourse/v6/atc/db"
     9  	"github.com/pf-qiu/concourse/v6/atc/db/dbfakes"
    10  	"github.com/pf-qiu/concourse/v6/atc/db/dbtest"
    11  
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  )
    15  
    16  var _ = Describe("WorkerFactory", func() {
    17  	var (
    18  		atcWorker atc.Worker
    19  		worker    db.Worker
    20  	)
    21  
    22  	BeforeEach(func() {
    23  		atcWorker = atc.Worker{
    24  			GardenAddr:       "some-garden-addr",
    25  			BaggageclaimURL:  "some-bc-url",
    26  			HTTPProxyURL:     "some-http-proxy-url",
    27  			HTTPSProxyURL:    "some-https-proxy-url",
    28  			NoProxy:          "some-no-proxy",
    29  			Ephemeral:        true,
    30  			ActiveContainers: 140,
    31  			ActiveVolumes:    550,
    32  			ResourceTypes: []atc.WorkerResourceType{
    33  				{
    34  					Type:       "some-resource-type",
    35  					Image:      "some-image",
    36  					Version:    "some-version",
    37  					Privileged: true,
    38  				},
    39  				{
    40  					Type:       "other-resource-type",
    41  					Image:      "other-image",
    42  					Version:    "other-version",
    43  					Privileged: false,
    44  				},
    45  			},
    46  			Platform:  "some-platform",
    47  			Tags:      atc.Tags{"some", "tags"},
    48  			Name:      "some-name",
    49  			StartTime: 1565367209,
    50  		}
    51  	})
    52  
    53  	Describe("SaveWorker", func() {
    54  		resourceTypeIDs := func(workerName string) map[string]int {
    55  			ids := map[string]int{}
    56  			rows, err := psql.Select("w.id", "b.name").
    57  				From("worker_base_resource_types w").
    58  				Join("base_resource_types AS b ON w.base_resource_type_id = b.id").
    59  				Where(sq.Eq{"w.worker_name": workerName}).
    60  				RunWith(dbConn).
    61  				Query()
    62  			Expect(err).NotTo(HaveOccurred())
    63  			for rows.Next() {
    64  				var id int
    65  				var name string
    66  				err = rows.Scan(&id, &name)
    67  				Expect(err).NotTo(HaveOccurred())
    68  				ids[name] = id
    69  			}
    70  			return ids
    71  		}
    72  
    73  		Context("when the worker already exists", func() {
    74  			BeforeEach(func() {
    75  				var err error
    76  				worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
    77  				Expect(err).NotTo(HaveOccurred())
    78  			})
    79  
    80  			It("saves resource types", func() {
    81  				worker, found, err := workerFactory.GetWorker(atcWorker.Name)
    82  				Expect(found).To(BeTrue())
    83  				Expect(err).NotTo(HaveOccurred())
    84  
    85  				Expect(worker.ResourceTypes()).To(Equal(atcWorker.ResourceTypes))
    86  			})
    87  
    88  			It("removes old worker resource type", func() {
    89  				atcWorker.ResourceTypes = []atc.WorkerResourceType{
    90  					{
    91  						Type:       "other-resource-type",
    92  						Image:      "other-image",
    93  						Version:    "other-version",
    94  						Privileged: false,
    95  					},
    96  				}
    97  
    98  				_, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
    99  				Expect(err).NotTo(HaveOccurred())
   100  
   101  				var count int
   102  				err = psql.Select("count(*)").
   103  					From("worker_base_resource_types").
   104  					Where(sq.Eq{"worker_name": "some-name"}).
   105  					RunWith(dbConn).
   106  					QueryRow().Scan(&count)
   107  				Expect(err).NotTo(HaveOccurred())
   108  				Expect(count).To(Equal(1))
   109  			})
   110  
   111  			It("replaces outdated worker resource type image", func() {
   112  				beforeIDs := resourceTypeIDs("some-name")
   113  				Expect(len(beforeIDs)).To(Equal(2))
   114  
   115  				atcWorker.ResourceTypes[0].Image = "some-wild-new-image"
   116  
   117  				_, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   118  				Expect(err).NotTo(HaveOccurred())
   119  
   120  				afterIDs := resourceTypeIDs("some-name")
   121  				Expect(len(afterIDs)).To(Equal(2))
   122  
   123  				Expect(afterIDs).ToNot(Equal(beforeIDs))
   124  
   125  				Expect(beforeIDs["some-resource-type"]).ToNot(Equal(afterIDs["some-resource-type"]))
   126  				Expect(beforeIDs["other-resource-type"]).To(Equal(afterIDs["other-resource-type"]))
   127  			})
   128  
   129  			It("replaces outdated worker resource type version", func() {
   130  				beforeIDs := resourceTypeIDs("some-name")
   131  				Expect(len(beforeIDs)).To(Equal(2))
   132  
   133  				atcWorker.ResourceTypes[0].Version = "some-wild-new-version"
   134  
   135  				_, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   136  				Expect(err).NotTo(HaveOccurred())
   137  
   138  				afterIDs := resourceTypeIDs("some-name")
   139  				Expect(len(afterIDs)).To(Equal(2))
   140  
   141  				Expect(afterIDs).ToNot(Equal(beforeIDs))
   142  
   143  				Expect(beforeIDs["some-resource-type"]).ToNot(Equal(afterIDs["some-resource-type"]))
   144  				Expect(beforeIDs["other-resource-type"]).To(Equal(afterIDs["other-resource-type"]))
   145  			})
   146  
   147  			Context("when the worker is in stalled state", func() {
   148  				BeforeEach(func() {
   149  					_, err := workerFactory.SaveWorker(atcWorker, -5*time.Minute)
   150  					Expect(err).NotTo(HaveOccurred())
   151  
   152  					_, err = workerLifecycle.StallUnresponsiveWorkers()
   153  					Expect(err).NotTo(HaveOccurred())
   154  				})
   155  
   156  				It("repopulates the garden address", func() {
   157  					savedWorker, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   158  					Expect(err).NotTo(HaveOccurred())
   159  					Expect(savedWorker.Name()).To(Equal("some-name"))
   160  					Expect(*savedWorker.GardenAddr()).To(Equal("some-garden-addr"))
   161  					Expect(savedWorker.State()).To(Equal(db.WorkerStateRunning))
   162  				})
   163  			})
   164  
   165  			Context("when the worker has a new version", func() {
   166  				BeforeEach(func() {
   167  					atcWorker.Version = "1.0.0"
   168  				})
   169  
   170  				It("updates the version of the worker", func() {
   171  					savedWorker, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   172  					Expect(err).NotTo(HaveOccurred())
   173  					Expect(worker.Version()).To(BeNil())
   174  					Expect(*savedWorker.Version()).To(Equal("1.0.0"))
   175  				})
   176  			})
   177  		})
   178  
   179  		Context("no worker with same name exists", func() {
   180  			BeforeEach(func() {
   181  				atcWorker.Version = "1.0.0"
   182  			})
   183  
   184  			It("saves worker", func() {
   185  				savedWorker, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   186  				Expect(err).NotTo(HaveOccurred())
   187  				Expect(savedWorker.Name()).To(Equal("some-name"))
   188  				Expect(*savedWorker.GardenAddr()).To(Equal("some-garden-addr"))
   189  				Expect(savedWorker.State()).To(Equal(db.WorkerStateRunning))
   190  				Expect(*savedWorker.Version()).To(Equal("1.0.0"))
   191  			})
   192  
   193  			It("saves worker resource types as base resource types", func() {
   194  				_, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   195  				Expect(err).NotTo(HaveOccurred())
   196  
   197  				var count int
   198  				err = psql.Select("count(*)").
   199  					From("worker_base_resource_types").
   200  					Where(sq.Eq{"worker_name": "some-name"}).
   201  					RunWith(dbConn).
   202  					QueryRow().Scan(&count)
   203  				Expect(err).NotTo(HaveOccurred())
   204  				Expect(count).To(Equal(2))
   205  			})
   206  		})
   207  	})
   208  
   209  	Describe("GetWorker", func() {
   210  		Context("when the worker is present", func() {
   211  			BeforeEach(func() {
   212  				_, err := workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   213  				Expect(err).NotTo(HaveOccurred())
   214  			})
   215  
   216  			It("finds the worker", func() {
   217  				foundWorker, found, err := workerFactory.GetWorker("some-name")
   218  				Expect(err).NotTo(HaveOccurred())
   219  				Expect(found).To(BeTrue())
   220  
   221  				Expect(foundWorker.Name()).To(Equal("some-name"))
   222  				Expect(*foundWorker.GardenAddr()).To(Equal("some-garden-addr"))
   223  				Expect(foundWorker.State()).To(Equal(db.WorkerStateRunning))
   224  				Expect(*foundWorker.BaggageclaimURL()).To(Equal("some-bc-url"))
   225  				Expect(foundWorker.HTTPProxyURL()).To(Equal("some-http-proxy-url"))
   226  				Expect(foundWorker.HTTPSProxyURL()).To(Equal("some-https-proxy-url"))
   227  				Expect(foundWorker.NoProxy()).To(Equal("some-no-proxy"))
   228  				Expect(foundWorker.Ephemeral()).To(Equal(true))
   229  				Expect(foundWorker.ActiveContainers()).To(Equal(140))
   230  				Expect(foundWorker.ActiveVolumes()).To(Equal(550))
   231  				Expect(foundWorker.ResourceTypes()).To(Equal([]atc.WorkerResourceType{
   232  					{
   233  						Type:       "some-resource-type",
   234  						Image:      "some-image",
   235  						Version:    "some-version",
   236  						Privileged: true,
   237  					},
   238  					{
   239  						Type:    "other-resource-type",
   240  						Image:   "other-image",
   241  						Version: "other-version",
   242  					},
   243  				}))
   244  				Expect(foundWorker.Platform()).To(Equal("some-platform"))
   245  				Expect(foundWorker.Tags()).To(Equal([]string{"some", "tags"}))
   246  				Expect(foundWorker.StartTime().Unix()).To(Equal(int64(1565367209)))
   247  				Expect(foundWorker.State()).To(Equal(db.WorkerStateRunning))
   248  			})
   249  
   250  			Context("when worker is stalled", func() {
   251  				BeforeEach(func() {
   252  					_, err := workerFactory.SaveWorker(atcWorker, -1*time.Minute)
   253  					Expect(err).NotTo(HaveOccurred())
   254  					stalled, err := workerLifecycle.StallUnresponsiveWorkers()
   255  					Expect(err).NotTo(HaveOccurred())
   256  					Expect(stalled).To(ContainElement("some-name"))
   257  				})
   258  			})
   259  		})
   260  
   261  		Context("when the worker is not present", func() {
   262  			It("returns false but no error", func() {
   263  				foundWorker, found, err := workerFactory.GetWorker("some-name")
   264  				Expect(err).NotTo(HaveOccurred())
   265  				Expect(found).To(BeFalse())
   266  				Expect(foundWorker).To(BeNil())
   267  			})
   268  		})
   269  	})
   270  
   271  	Describe("VisibleWorkers", func() {
   272  		BeforeEach(func() {
   273  			postgresRunner.Truncate()
   274  		})
   275  
   276  		Context("when there are public and private workers on multiple teams", func() {
   277  			BeforeEach(func() {
   278  				team1, err := teamFactory.CreateTeam(atc.Team{Name: "some-team"})
   279  				Expect(err).NotTo(HaveOccurred())
   280  				team2, err := teamFactory.CreateTeam(atc.Team{Name: "some-other-team"})
   281  				Expect(err).NotTo(HaveOccurred())
   282  				team3, err := teamFactory.CreateTeam(atc.Team{Name: "not-this-team"})
   283  				Expect(err).NotTo(HaveOccurred())
   284  
   285  				_, err = workerFactory.SaveWorker(atcWorker, 0)
   286  				Expect(err).NotTo(HaveOccurred())
   287  
   288  				atcWorker.Name = "some-new-worker"
   289  				atcWorker.GardenAddr = "some-other-garden-addr"
   290  				atcWorker.BaggageclaimURL = "some-other-bc-url"
   291  				_, err = team1.SaveWorker(atcWorker, 0)
   292  				Expect(err).NotTo(HaveOccurred())
   293  
   294  				atcWorker.Name = "some-other-new-worker"
   295  				atcWorker.GardenAddr = "some-other-other-garden-addr"
   296  				atcWorker.BaggageclaimURL = "some-other-other-bc-url"
   297  				_, err = team2.SaveWorker(atcWorker, 0)
   298  				Expect(err).NotTo(HaveOccurred())
   299  
   300  				atcWorker.Name = "not-this-worker"
   301  				atcWorker.GardenAddr = "not-this-garden-addr"
   302  				atcWorker.BaggageclaimURL = "not-this-bc-url"
   303  				_, err = team3.SaveWorker(atcWorker, 0)
   304  				Expect(err).NotTo(HaveOccurred())
   305  			})
   306  
   307  			It("finds visble workers for the given teams", func() {
   308  				workers, err := workerFactory.VisibleWorkers([]string{"some-team", "some-other-team"})
   309  				Expect(err).NotTo(HaveOccurred())
   310  				Expect(len(workers)).To(Equal(3))
   311  
   312  				w1, found, err := workerFactory.GetWorker("some-name")
   313  				Expect(found).To(BeTrue())
   314  				Expect(err).NotTo(HaveOccurred())
   315  
   316  				w2, found, err := workerFactory.GetWorker("some-new-worker")
   317  				Expect(found).To(BeTrue())
   318  				Expect(err).NotTo(HaveOccurred())
   319  
   320  				w3, found, err := workerFactory.GetWorker("some-other-new-worker")
   321  				Expect(found).To(BeTrue())
   322  				Expect(err).NotTo(HaveOccurred())
   323  
   324  				w4, found, err := workerFactory.GetWorker("not-this-worker")
   325  				Expect(found).To(BeTrue())
   326  				Expect(err).NotTo(HaveOccurred())
   327  
   328  				Expect(workers).To(ConsistOf(w1, w2, w3))
   329  				Expect(workers).NotTo(ContainElement(w4))
   330  			})
   331  		})
   332  
   333  		Context("when there are no workers", func() {
   334  			It("returns an error", func() {
   335  				workers, err := workerFactory.VisibleWorkers([]string{"some-team"})
   336  				Expect(err).NotTo(HaveOccurred())
   337  				Expect(workers).To(BeEmpty())
   338  			})
   339  		})
   340  	})
   341  
   342  	Describe("Workers", func() {
   343  		BeforeEach(func() {
   344  			postgresRunner.Truncate()
   345  		})
   346  
   347  		Context("when there are workers", func() {
   348  			BeforeEach(func() {
   349  				_, err := workerFactory.SaveWorker(atcWorker, 0)
   350  				Expect(err).NotTo(HaveOccurred())
   351  
   352  				atcWorker.Name = "some-new-worker"
   353  				atcWorker.GardenAddr = "some-other-garden-addr"
   354  				atcWorker.BaggageclaimURL = "some-other-bc-url"
   355  				_, err = workerFactory.SaveWorker(atcWorker, 0)
   356  				Expect(err).NotTo(HaveOccurred())
   357  			})
   358  
   359  			It("finds them without error", func() {
   360  				workers, err := workerFactory.Workers()
   361  				Expect(err).NotTo(HaveOccurred())
   362  				Expect(len(workers)).To(Equal(2))
   363  
   364  				strptr := func(s string) *string {
   365  					return &s
   366  				}
   367  
   368  				Expect(workers).To(ConsistOf(
   369  					And(
   370  						WithTransform((db.Worker).Name, Equal("some-name")),
   371  						WithTransform((db.Worker).GardenAddr, Equal(strptr("some-garden-addr"))),
   372  						WithTransform((db.Worker).BaggageclaimURL, Equal(strptr("some-bc-url"))),
   373  					),
   374  					And(
   375  						WithTransform((db.Worker).Name, Equal("some-new-worker")),
   376  						WithTransform((db.Worker).GardenAddr, Equal(strptr("some-other-garden-addr"))),
   377  						WithTransform((db.Worker).BaggageclaimURL, Equal(strptr("some-other-bc-url"))),
   378  					),
   379  				))
   380  			})
   381  		})
   382  
   383  		Context("when there are no workers", func() {
   384  			It("returns an error", func() {
   385  				workers, err := workerFactory.Workers()
   386  				Expect(err).NotTo(HaveOccurred())
   387  				Expect(workers).To(BeEmpty())
   388  			})
   389  		})
   390  	})
   391  
   392  	Describe("HeartbeatWorker", func() {
   393  		var (
   394  			ttl              time.Duration
   395  			epsilon          time.Duration
   396  			activeContainers int
   397  			activeVolumes    int
   398  		)
   399  
   400  		BeforeEach(func() {
   401  			ttl = 5 * time.Minute
   402  			epsilon = 30 * time.Second
   403  			activeContainers = 0
   404  			activeVolumes = 0
   405  
   406  			atcWorker.ActiveContainers = activeContainers
   407  			atcWorker.ActiveVolumes = activeVolumes
   408  		})
   409  
   410  		Context("when the worker is present", func() {
   411  			JustBeforeEach(func() {
   412  				_, err := workerFactory.SaveWorker(atcWorker, 1*time.Second)
   413  				Expect(err).NotTo(HaveOccurred())
   414  			})
   415  
   416  			It("updates the expires field, and the number of active containers and volumes", func() {
   417  				atcWorker.ActiveContainers = 1
   418  				atcWorker.ActiveVolumes = 3
   419  
   420  				now := time.Now()
   421  				By("current time")
   422  				By(now.String())
   423  				later := now.Add(ttl)
   424  				By("later time")
   425  				By(later.String())
   426  				By("found worker expiry")
   427  				foundWorker, err := workerFactory.HeartbeatWorker(atcWorker, ttl)
   428  				Expect(err).NotTo(HaveOccurred())
   429  				By(foundWorker.ExpiresAt().String())
   430  
   431  				Expect(foundWorker.Name()).To(Equal(atcWorker.Name))
   432  				Expect(foundWorker.ExpiresAt()).To(BeTemporally("~", later, epsilon))
   433  				Expect(foundWorker.ActiveContainers()).To(And(Not(Equal(activeContainers)), Equal(1)))
   434  				Expect(foundWorker.ActiveVolumes()).To(And(Not(Equal(activeVolumes)), Equal(3)))
   435  				Expect(*foundWorker.GardenAddr()).To(Equal("some-garden-addr"))
   436  				Expect(*foundWorker.BaggageclaimURL()).To(Equal("some-bc-url"))
   437  			})
   438  
   439  			Context("when the current state is landing", func() {
   440  				BeforeEach(func() {
   441  					atcWorker.State = string(db.WorkerStateLanding)
   442  				})
   443  
   444  				It("keeps the state as landing", func() {
   445  					foundWorker, err := workerFactory.HeartbeatWorker(atcWorker, ttl)
   446  					Expect(err).NotTo(HaveOccurred())
   447  
   448  					Expect(foundWorker.State()).To(Equal(db.WorkerStateLanding))
   449  				})
   450  			})
   451  
   452  			Context("when the current state is retiring", func() {
   453  				BeforeEach(func() {
   454  					atcWorker.State = string(db.WorkerStateRetiring)
   455  				})
   456  
   457  				It("keeps the state as retiring", func() {
   458  					foundWorker, err := workerFactory.HeartbeatWorker(atcWorker, ttl)
   459  					Expect(err).NotTo(HaveOccurred())
   460  
   461  					Expect(foundWorker.State()).To(Equal(db.WorkerStateRetiring))
   462  				})
   463  			})
   464  
   465  			Context("when the current state is running", func() {
   466  				BeforeEach(func() {
   467  					atcWorker.State = string(db.WorkerStateRunning)
   468  				})
   469  
   470  				It("keeps the state as running", func() {
   471  					foundWorker, err := workerFactory.HeartbeatWorker(atcWorker, ttl)
   472  					Expect(err).NotTo(HaveOccurred())
   473  
   474  					Expect(foundWorker.State()).To(Equal(db.WorkerStateRunning))
   475  				})
   476  			})
   477  
   478  			Context("when the current state is stalled", func() {
   479  				var (
   480  					unresponsiveWorker db.Worker
   481  					err                error
   482  				)
   483  
   484  				JustBeforeEach(func() {
   485  					unresponsiveWorker, err = workerFactory.SaveWorker(atcWorker, -5*time.Minute)
   486  					Expect(err).NotTo(HaveOccurred())
   487  
   488  					_, err = workerLifecycle.StallUnresponsiveWorkers()
   489  					Expect(err).NotTo(HaveOccurred())
   490  				})
   491  
   492  				It("sets the state as running", func() {
   493  					stalledWorker, found, err := workerFactory.GetWorker(unresponsiveWorker.Name())
   494  					Expect(err).NotTo(HaveOccurred())
   495  					Expect(found).To(BeTrue())
   496  
   497  					Expect(stalledWorker.State()).To(Equal(db.WorkerStateStalled))
   498  
   499  					foundWorker, err := workerFactory.HeartbeatWorker(atcWorker, ttl)
   500  					Expect(err).NotTo(HaveOccurred())
   501  
   502  					Expect(foundWorker.State()).To(Equal(db.WorkerStateRunning))
   503  				})
   504  			})
   505  		})
   506  
   507  		Context("when the worker is not present", func() {
   508  			It("returns an error", func() {
   509  				foundWorker, err := workerFactory.HeartbeatWorker(atcWorker, ttl)
   510  				Expect(err).To(HaveOccurred())
   511  				Expect(err).To(Equal(db.ErrWorkerNotPresent))
   512  				Expect(foundWorker).To(BeNil())
   513  			})
   514  		})
   515  	})
   516  
   517  	Describe("FindWorkerForContainerByOwner", func() {
   518  		var (
   519  			containerMetadata db.ContainerMetadata
   520  			build             db.Build
   521  			fakeOwner         *dbfakes.FakeContainerOwner
   522  			otherFakeOwner    *dbfakes.FakeContainerOwner
   523  		)
   524  
   525  		BeforeEach(func() {
   526  			var err error
   527  			containerMetadata = db.ContainerMetadata{
   528  				Type:     "task",
   529  				StepName: "some-task",
   530  			}
   531  			build, err = defaultTeam.CreateOneOffBuild()
   532  			Expect(err).ToNot(HaveOccurred())
   533  
   534  			fakeOwner = new(dbfakes.FakeContainerOwner)
   535  			fakeOwner.FindReturns(sq.Eq{
   536  				"build_id": build.ID(),
   537  				"plan_id":  "simple-plan",
   538  				"team_id":  1,
   539  			}, true, nil)
   540  			fakeOwner.CreateReturns(map[string]interface{}{
   541  				"build_id": build.ID(),
   542  				"plan_id":  "simple-plan",
   543  				"team_id":  1,
   544  			}, nil)
   545  
   546  			otherFakeOwner = new(dbfakes.FakeContainerOwner)
   547  			otherFakeOwner.FindReturns(sq.Eq{
   548  				"build_id": build.ID(),
   549  				"plan_id":  "simple-plan",
   550  				"team_id":  2,
   551  			}, true, nil)
   552  			otherFakeOwner.CreateReturns(map[string]interface{}{
   553  				"build_id": build.ID(),
   554  				"plan_id":  "simple-plan",
   555  				"team_id":  2,
   556  			}, nil)
   557  		})
   558  
   559  		Context("when there are check containers", func() {
   560  			Context("when there are multiple of the same containers on the global, team and tagged worker", func() {
   561  				var (
   562  					scenario *dbtest.Scenario
   563  				)
   564  
   565  				BeforeEach(func() {
   566  					scenario = dbtest.Setup(
   567  						builder.WithPipeline(atc.Config{
   568  							Resources: atc.ResourceConfigs{
   569  								{
   570  									Name: "some-resource",
   571  									Type: "some-base-resource-type",
   572  									Source: atc.Source{
   573  										"some": "source",
   574  									},
   575  								},
   576  							},
   577  						}),
   578  						builder.WithWorker(atc.Worker{
   579  							ResourceTypes:   []atc.WorkerResourceType{defaultWorkerResourceType},
   580  							GardenAddr:      "some-tagged-garden-addr",
   581  							BaggageclaimURL: "some-tagged-bc-url",
   582  							Name:            "some-tagged-name",
   583  							Tags:            []string{"some-tag"},
   584  						}),
   585  						builder.WithWorker(atc.Worker{
   586  							ResourceTypes:   []atc.WorkerResourceType{defaultWorkerResourceType},
   587  							GardenAddr:      "some-team-garden-addr",
   588  							BaggageclaimURL: "some-team-bc-url",
   589  							Name:            "some-team-name",
   590  							Team:            "default-team",
   591  						}),
   592  						builder.WithWorker(atc.Worker{
   593  							ResourceTypes:   []atc.WorkerResourceType{defaultWorkerResourceType},
   594  							GardenAddr:      "some-other-garden-addr",
   595  							BaggageclaimURL: "some-other-bc-url",
   596  							Name:            "some-other-name",
   597  						}),
   598  						builder.WithResourceVersions(
   599  							"some-resource",
   600  						),
   601  						builder.WithCheckContainer(
   602  							"some-resource",
   603  							"some-other-name",
   604  						),
   605  						builder.WithCheckContainer(
   606  							"some-resource",
   607  							"some-tagged-name",
   608  						),
   609  						builder.WithCheckContainer(
   610  							"some-resource",
   611  							"some-team-name",
   612  						),
   613  					)
   614  				})
   615  
   616  				It("should find all the workers that have the same container", func() {
   617  					resource, found, err := scenario.Pipeline.Resource("some-resource")
   618  					Expect(err).ToNot(HaveOccurred())
   619  					Expect(found).To(BeTrue(), "resource '%s' not found", "some-resource")
   620  
   621  					rc, found, err := resourceConfigFactory.FindResourceConfigByID(resource.ResourceConfigID())
   622  					Expect(err).ToNot(HaveOccurred())
   623  					Expect(found).To(BeTrue(), "resource config '%s' not found", rc.ID())
   624  
   625  					owner := db.NewResourceConfigCheckSessionContainerOwner(
   626  						rc.ID(),
   627  						rc.OriginBaseResourceType().ID,
   628  						db.ContainerOwnerExpiries{
   629  							Min: 5 * time.Minute,
   630  							Max: 5 * time.Minute,
   631  						},
   632  					)
   633  
   634  					workers, err := workerFactory.FindWorkersForContainerByOwner(owner)
   635  					Expect(err).ToNot(HaveOccurred())
   636  
   637  					var workerNames []string
   638  					for _, w := range workers {
   639  						workerNames = append(workerNames, w.Name())
   640  					}
   641  
   642  					Expect(workerNames).To(ConsistOf([]string{"some-other-name", "some-tagged-name", "some-team-name"}))
   643  				})
   644  			})
   645  		})
   646  
   647  		Context("when there are build containers", func() {
   648  			Context("when there is a creating container", func() {
   649  				BeforeEach(func() {
   650  					_, err := defaultWorker.CreateContainer(fakeOwner, containerMetadata)
   651  					Expect(err).ToNot(HaveOccurred())
   652  				})
   653  
   654  				It("returns it", func() {
   655  					workers, err := workerFactory.FindWorkersForContainerByOwner(fakeOwner)
   656  					Expect(err).ToNot(HaveOccurred())
   657  					Expect(workers).To(HaveLen(1))
   658  					Expect(workers[0].Name()).To(Equal(defaultWorker.Name()))
   659  				})
   660  
   661  				It("does not find container for another team", func() {
   662  					workers, err := workerFactory.FindWorkersForContainerByOwner(otherFakeOwner)
   663  					Expect(err).ToNot(HaveOccurred())
   664  					Expect(workers).To(HaveLen(0))
   665  				})
   666  			})
   667  
   668  			Context("when there is a created container", func() {
   669  				BeforeEach(func() {
   670  					creatingContainer, err := defaultWorker.CreateContainer(fakeOwner, containerMetadata)
   671  					Expect(err).ToNot(HaveOccurred())
   672  
   673  					_, err = creatingContainer.Created()
   674  					Expect(err).ToNot(HaveOccurred())
   675  				})
   676  
   677  				It("returns it", func() {
   678  					workers, err := workerFactory.FindWorkersForContainerByOwner(fakeOwner)
   679  					Expect(err).ToNot(HaveOccurred())
   680  					Expect(workers).To(HaveLen(1))
   681  					Expect(workers[0].Name()).To(Equal(defaultWorker.Name()))
   682  				})
   683  
   684  				It("does not find container for another team", func() {
   685  					workers, err := workerFactory.FindWorkersForContainerByOwner(otherFakeOwner)
   686  					Expect(err).ToNot(HaveOccurred())
   687  					Expect(workers).To(HaveLen(0))
   688  				})
   689  			})
   690  
   691  			Context("when there is no container", func() {
   692  				It("returns nil", func() {
   693  					bogusOwner := new(dbfakes.FakeContainerOwner)
   694  					bogusOwner.FindReturns(sq.Eq{
   695  						"build_id": build.ID() + 1,
   696  						"plan_id":  "how-could-this-happen-to-me",
   697  						"team_id":  1,
   698  					}, true, nil)
   699  					bogusOwner.CreateReturns(map[string]interface{}{
   700  						"build_id": build.ID() + 1,
   701  						"plan_id":  "how-could-this-happen-to-me",
   702  						"team_id":  1,
   703  					}, nil)
   704  
   705  					workers, err := workerFactory.FindWorkersForContainerByOwner(bogusOwner)
   706  					Expect(err).ToNot(HaveOccurred())
   707  					Expect(workers).To(HaveLen(0))
   708  				})
   709  			})
   710  		})
   711  	})
   712  
   713  	Describe("BuildContainersCountPerWorker", func() {
   714  		var (
   715  			fakeOwner      *dbfakes.FakeContainerOwner
   716  			otherFakeOwner *dbfakes.FakeContainerOwner
   717  			build          db.Build
   718  		)
   719  
   720  		BeforeEach(func() {
   721  			var err error
   722  
   723  			build, err = defaultTeam.CreateOneOffBuild()
   724  			Expect(err).ToNot(HaveOccurred())
   725  
   726  			worker, err = workerFactory.SaveWorker(atcWorker, 5*time.Minute)
   727  			Expect(err).ToNot(HaveOccurred())
   728  
   729  			fakeOwner = new(dbfakes.FakeContainerOwner)
   730  			fakeOwner.FindReturns(sq.Eq{
   731  				"build_id": build.ID(),
   732  				"plan_id":  "simple-plan",
   733  				"team_id":  1,
   734  			}, true, nil)
   735  			fakeOwner.CreateReturns(map[string]interface{}{
   736  				"build_id": build.ID(),
   737  				"plan_id":  "simple-plan",
   738  				"team_id":  1,
   739  			}, nil)
   740  
   741  			otherFakeOwner = new(dbfakes.FakeContainerOwner)
   742  			otherFakeOwner.FindReturns(sq.Eq{
   743  				"build_id": nil,
   744  				"plan_id":  "simple-plan",
   745  				"team_id":  1,
   746  			}, true, nil)
   747  			otherFakeOwner.CreateReturns(map[string]interface{}{
   748  				"build_id": nil,
   749  				"plan_id":  "simple-plan",
   750  				"team_id":  1,
   751  			}, nil)
   752  
   753  			creatingContainer, err := defaultWorker.CreateContainer(fakeOwner, db.ContainerMetadata{
   754  				Type:     "task",
   755  				StepName: "some-task",
   756  			})
   757  			Expect(err).ToNot(HaveOccurred())
   758  
   759  			_, err = creatingContainer.Created()
   760  			Expect(err).ToNot(HaveOccurred())
   761  
   762  			_, err = defaultWorker.CreateContainer(otherFakeOwner, db.ContainerMetadata{
   763  				Type: "check",
   764  			})
   765  			Expect(err).ToNot(HaveOccurred())
   766  
   767  			_, err = worker.CreateContainer(fakeOwner, db.ContainerMetadata{
   768  				Type:     "task",
   769  				StepName: "other-task",
   770  			})
   771  			Expect(err).ToNot(HaveOccurred())
   772  
   773  			_, err = worker.CreateContainer(otherFakeOwner, db.ContainerMetadata{
   774  				Type: "check",
   775  			})
   776  			Expect(err).ToNot(HaveOccurred())
   777  		})
   778  
   779  		It("returns a map of worker to number of active build containers", func() {
   780  			containersCountByWorker, err := workerFactory.BuildContainersCountPerWorker()
   781  			Expect(err).ToNot(HaveOccurred())
   782  
   783  			Expect(containersCountByWorker).To(HaveLen(2))
   784  			Expect(containersCountByWorker[defaultWorker.Name()]).To(Equal(1))
   785  			Expect(containersCountByWorker[worker.Name()]).To(Equal(1))
   786  		})
   787  	})
   788  })