1package db_test
2
3import (
4	"github.com/concourse/concourse/atc"
5	"github.com/concourse/concourse/atc/db"
6	. "github.com/onsi/ginkgo"
7	. "github.com/onsi/gomega"
8	. "github.com/onsi/gomega/gstruct"
9)
10
11var _ = Describe("Job Factory", func() {
12	var jobFactory db.JobFactory
13
14	BeforeEach(func() {
15		jobFactory = db.NewJobFactory(dbConn, lockFactory)
16	})
17
18	Context("when there are public and private pipelines", func() {
19		var publicPipeline db.Pipeline
20
21		BeforeEach(func() {
22			otherTeam, err := teamFactory.CreateTeam(atc.Team{Name: "other-team"})
23			Expect(err).NotTo(HaveOccurred())
24
25			publicPipeline, _, err = otherTeam.SavePipeline("public-pipeline", atc.Config{
26				Jobs: atc.JobConfigs{
27					{
28						Name: "public-pipeline-job-1",
29						PlanSequence: []atc.Step{
30							{
31								Config: &atc.GetStep{
32									Name: "some-resource",
33								},
34							},
35							{
36								Config: &atc.GetStep{
37									Name: "some-other-resource",
38								},
39							},
40							{
41								Config: &atc.PutStep{
42									Name: "some-resource",
43								},
44							},
45						},
46					},
47					{
48						Name: "public-pipeline-job-2",
49						PlanSequence: []atc.Step{
50							{
51								Config: &atc.GetStep{
52									Name:   "some-resource",
53									Passed: []string{"public-pipeline-job-1"},
54								},
55							},
56							{
57								Config: &atc.GetStep{
58									Name:   "some-other-resource",
59									Passed: []string{"public-pipeline-job-1"},
60								},
61							},
62							{
63								Config: &atc.GetStep{
64									Name:     "resource",
65									Resource: "some-resource",
66								},
67							},
68							{
69								Config: &atc.PutStep{
70									Name:     "resource",
71									Resource: "some-resource",
72								},
73							},
74							{
75								Config: &atc.PutStep{
76									Name: "some-resource",
77								},
78							},
79						},
80					},
81					{
82						Name: "public-pipeline-job-3",
83						PlanSequence: []atc.Step{
84							{
85								Config: &atc.GetStep{
86									Name:   "some-resource",
87									Passed: []string{"public-pipeline-job-1", "public-pipeline-job-2"},
88								},
89							},
90						},
91					},
92				},
93				Resources: atc.ResourceConfigs{
94					{
95						Name: "some-resource",
96						Type: "some-type",
97					},
98					{
99						Name: "some-other-resource",
100						Type: "some-type",
101					},
102				},
103			}, db.ConfigVersion(0), false)
104			Expect(err).ToNot(HaveOccurred())
105			Expect(publicPipeline.Expose()).To(Succeed())
106
107			_, _, err = otherTeam.SavePipeline("private-pipeline", atc.Config{
108				Jobs: atc.JobConfigs{
109					{
110						Name: "private-pipeline-job",
111						PlanSequence: []atc.Step{
112							{
113								Config: &atc.GetStep{
114									Name: "some-resource",
115								},
116							},
117							{
118								Config: &atc.PutStep{
119									Name: "some-resource",
120								},
121							},
122						},
123					},
124				},
125				Resources: atc.ResourceConfigs{
126					{
127						Name: "some-resource",
128						Type: "some-type",
129					},
130				},
131			}, db.ConfigVersion(0), false)
132			Expect(err).ToNot(HaveOccurred())
133		})
134
135		Describe("VisibleJobs", func() {
136			It("returns jobs in the provided teams and jobs in public pipelines", func() {
137				visibleJobs, err := jobFactory.VisibleJobs([]string{"default-team"})
138				Expect(err).ToNot(HaveOccurred())
139
140				Expect(len(visibleJobs)).To(Equal(4))
141				Expect(visibleJobs[0].Name).To(Equal("some-job"))
142				Expect(visibleJobs[1].Name).To(Equal("public-pipeline-job-1"))
143				Expect(visibleJobs[2].Name).To(Equal("public-pipeline-job-2"))
144				Expect(visibleJobs[3].Name).To(Equal("public-pipeline-job-3"))
145
146				Expect(visibleJobs[0].Inputs).To(BeNil())
147				Expect(visibleJobs[1].Inputs).To(Equal([]atc.DashboardJobInput{
148					atc.DashboardJobInput{
149						Name:     "some-other-resource",
150						Resource: "some-other-resource",
151					},
152					atc.DashboardJobInput{
153						Name:     "some-resource",
154						Resource: "some-resource",
155					},
156				}))
157				Expect(visibleJobs[2].Inputs).To(Equal([]atc.DashboardJobInput{
158					atc.DashboardJobInput{
159						Name:     "resource",
160						Resource: "some-resource",
161					},
162					atc.DashboardJobInput{
163						Name:     "some-other-resource",
164						Resource: "some-other-resource",
165						Passed:   []string{"public-pipeline-job-1"},
166					},
167					atc.DashboardJobInput{
168						Name:     "some-resource",
169						Resource: "some-resource",
170						Passed:   []string{"public-pipeline-job-1"},
171					},
172				}))
173				Expect(visibleJobs[3].Inputs).To(Equal([]atc.DashboardJobInput{
174					atc.DashboardJobInput{
175						Name:     "some-resource",
176						Resource: "some-resource",
177						Passed:   []string{"public-pipeline-job-1", "public-pipeline-job-2"},
178					},
179				}))
180
181				Expect(visibleJobs[0].Outputs).To(BeNil())
182				Expect(visibleJobs[1].Outputs).To(Equal([]atc.JobOutput{
183					atc.JobOutput{
184						Name:     "some-resource",
185						Resource: "some-resource",
186					},
187				}))
188				Expect(visibleJobs[2].Outputs).To(Equal([]atc.JobOutput{
189					atc.JobOutput{
190						Name:     "resource",
191						Resource: "some-resource",
192					},
193					atc.JobOutput{
194						Name:     "some-resource",
195						Resource: "some-resource",
196					},
197				}))
198				Expect(visibleJobs[3].Outputs).To(BeNil())
199			})
200
201			It("returns next build, latest completed build, and transition build for each job", func() {
202				job, found, err := defaultPipeline.Job("some-job")
203				Expect(err).ToNot(HaveOccurred())
204				Expect(found).To(BeTrue())
205
206				transitionBuild, err := job.CreateBuild()
207				Expect(err).ToNot(HaveOccurred())
208
209				err = transitionBuild.Finish(db.BuildStatusSucceeded)
210				Expect(err).ToNot(HaveOccurred())
211
212				found, err = transitionBuild.Reload()
213				Expect(err).ToNot(HaveOccurred())
214				Expect(found).To(BeTrue())
215
216				finishedBuild, err := job.CreateBuild()
217				Expect(err).ToNot(HaveOccurred())
218
219				err = finishedBuild.Finish(db.BuildStatusSucceeded)
220				Expect(err).ToNot(HaveOccurred())
221
222				found, err = finishedBuild.Reload()
223				Expect(err).ToNot(HaveOccurred())
224				Expect(found).To(BeTrue())
225
226				nextBuild, err := job.CreateBuild()
227				Expect(err).ToNot(HaveOccurred())
228
229				visibleJobs, err := jobFactory.VisibleJobs([]string{"default-team"})
230				Expect(err).ToNot(HaveOccurred())
231
232				Expect(visibleJobs[0].Name).To(Equal("some-job"))
233				Expect(visibleJobs[0].NextBuild.ID).To(Equal(nextBuild.ID()))
234				Expect(visibleJobs[0].NextBuild.Name).To(Equal(nextBuild.Name()))
235				Expect(visibleJobs[0].NextBuild.JobName).To(Equal(nextBuild.JobName()))
236				Expect(visibleJobs[0].NextBuild.PipelineName).To(Equal(nextBuild.PipelineName()))
237				Expect(visibleJobs[0].NextBuild.TeamName).To(Equal(nextBuild.TeamName()))
238				Expect(visibleJobs[0].NextBuild.Status).To(Equal(string(nextBuild.Status())))
239				Expect(visibleJobs[0].NextBuild.StartTime).To(Equal(nextBuild.StartTime()))
240				Expect(visibleJobs[0].NextBuild.EndTime).To(Equal(nextBuild.EndTime()))
241
242				Expect(visibleJobs[0].FinishedBuild.ID).To(Equal(finishedBuild.ID()))
243				Expect(visibleJobs[0].FinishedBuild.Name).To(Equal(finishedBuild.Name()))
244				Expect(visibleJobs[0].FinishedBuild.JobName).To(Equal(finishedBuild.JobName()))
245				Expect(visibleJobs[0].FinishedBuild.PipelineName).To(Equal(finishedBuild.PipelineName()))
246				Expect(visibleJobs[0].FinishedBuild.TeamName).To(Equal(finishedBuild.TeamName()))
247				Expect(visibleJobs[0].FinishedBuild.Status).To(Equal(string(finishedBuild.Status())))
248				Expect(visibleJobs[0].FinishedBuild.StartTime).To(Equal(finishedBuild.StartTime()))
249				Expect(visibleJobs[0].FinishedBuild.EndTime).To(Equal(finishedBuild.EndTime()))
250
251				Expect(visibleJobs[0].TransitionBuild.ID).To(Equal(transitionBuild.ID()))
252				Expect(visibleJobs[0].TransitionBuild.Name).To(Equal(transitionBuild.Name()))
253				Expect(visibleJobs[0].TransitionBuild.JobName).To(Equal(transitionBuild.JobName()))
254				Expect(visibleJobs[0].TransitionBuild.PipelineName).To(Equal(transitionBuild.PipelineName()))
255				Expect(visibleJobs[0].TransitionBuild.TeamName).To(Equal(transitionBuild.TeamName()))
256				Expect(visibleJobs[0].TransitionBuild.Status).To(Equal(string(transitionBuild.Status())))
257				Expect(visibleJobs[0].TransitionBuild.StartTime).To(Equal(transitionBuild.StartTime()))
258				Expect(visibleJobs[0].TransitionBuild.EndTime).To(Equal(transitionBuild.EndTime()))
259			})
260		})
261
262		Describe("AllActiveJobs", func() {
263			It("return all private and public pipelines", func() {
264				allJobs, err := jobFactory.AllActiveJobs()
265				Expect(err).ToNot(HaveOccurred())
266
267				Expect(len(allJobs)).To(Equal(5))
268				Expect(allJobs[0].Name).To(Equal("some-job"))
269				Expect(allJobs[1].Name).To(Equal("public-pipeline-job-1"))
270				Expect(allJobs[2].Name).To(Equal("public-pipeline-job-2"))
271				Expect(allJobs[3].Name).To(Equal("public-pipeline-job-3"))
272				Expect(allJobs[4].Name).To(Equal("private-pipeline-job"))
273
274				Expect(allJobs[0].Inputs).To(BeNil())
275				Expect(allJobs[1].Inputs).To(Equal([]atc.DashboardJobInput{
276					atc.DashboardJobInput{
277						Name:     "some-other-resource",
278						Resource: "some-other-resource",
279					},
280					atc.DashboardJobInput{
281						Name:     "some-resource",
282						Resource: "some-resource",
283					},
284				}))
285				Expect(allJobs[2].Inputs).To(Equal([]atc.DashboardJobInput{
286					atc.DashboardJobInput{
287						Name:     "resource",
288						Resource: "some-resource",
289					},
290					atc.DashboardJobInput{
291						Name:     "some-other-resource",
292						Resource: "some-other-resource",
293						Passed:   []string{"public-pipeline-job-1"},
294					},
295					atc.DashboardJobInput{
296						Name:     "some-resource",
297						Resource: "some-resource",
298						Passed:   []string{"public-pipeline-job-1"},
299					},
300				}))
301				Expect(allJobs[3].Inputs).To(Equal([]atc.DashboardJobInput{
302					atc.DashboardJobInput{
303						Name:     "some-resource",
304						Resource: "some-resource",
305						Passed:   []string{"public-pipeline-job-1", "public-pipeline-job-2"},
306					},
307				}))
308				Expect(allJobs[4].Inputs).To(Equal([]atc.DashboardJobInput{
309					atc.DashboardJobInput{
310						Name:     "some-resource",
311						Resource: "some-resource",
312					},
313				}))
314
315				Expect(allJobs[0].Outputs).To(BeNil())
316				Expect(allJobs[1].Outputs).To(Equal([]atc.JobOutput{
317					atc.JobOutput{
318						Name:     "some-resource",
319						Resource: "some-resource",
320					},
321				}))
322				Expect(allJobs[2].Outputs).To(Equal([]atc.JobOutput{
323					atc.JobOutput{
324						Name:     "resource",
325						Resource: "some-resource",
326					},
327					atc.JobOutput{
328						Name:     "some-resource",
329						Resource: "some-resource",
330					},
331				}))
332				Expect(allJobs[3].Outputs).To(BeNil())
333				Expect(allJobs[4].Outputs).To(Equal([]atc.JobOutput{
334					atc.JobOutput{
335						Name:     "some-resource",
336						Resource: "some-resource",
337					},
338				}))
339			})
340		})
341	})
342
343	Describe("JobsToSchedule", func() {
344		var (
345			job1 db.Job
346			job2 db.Job
347			job3 db.Job
348		)
349
350		BeforeEach(func() {
351			err := defaultPipeline.Destroy()
352			Expect(err).ToNot(HaveOccurred())
353		})
354
355		Context("when the job has a requested schedule time later than the last scheduled", func() {
356			BeforeEach(func() {
357				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
358					Jobs: atc.JobConfigs{
359						{Name: "job-name"},
360					},
361				}, db.ConfigVersion(1), false)
362				Expect(err).ToNot(HaveOccurred())
363
364				var found bool
365				job1, found, err = pipeline1.Job("job-name")
366				Expect(err).ToNot(HaveOccurred())
367				Expect(found).To(BeTrue())
368
369				err = job1.RequestSchedule()
370				Expect(err).ToNot(HaveOccurred())
371			})
372
373			It("fetches that job", func() {
374				jobs, err := jobFactory.JobsToSchedule()
375				Expect(err).ToNot(HaveOccurred())
376				Expect(len(jobs)).To(Equal(1))
377				Expect(jobs[0].Name()).To(Equal(job1.Name()))
378			})
379		})
380
381		Context("when the job has a requested schedule time earlier than the last scheduled", func() {
382			BeforeEach(func() {
383				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
384					Jobs: atc.JobConfigs{
385						{Name: "job-name"},
386					},
387				}, db.ConfigVersion(1), false)
388				Expect(err).ToNot(HaveOccurred())
389
390				var found bool
391				job1, found, err = pipeline1.Job("job-name")
392				Expect(err).ToNot(HaveOccurred())
393				Expect(found).To(BeTrue())
394
395				_, err = dbConn.Exec("UPDATE jobs SET last_scheduled = now() WHERE id = $1;", job1.ID())
396				Expect(err).ToNot(HaveOccurred())
397			})
398
399			It("does not fetch that job", func() {
400				jobs, err := jobFactory.JobsToSchedule()
401				Expect(err).ToNot(HaveOccurred())
402				Expect(len(jobs)).To(Equal(0))
403			})
404		})
405
406		Context("when the job has a requested schedule time is the same as the last scheduled", func() {
407			BeforeEach(func() {
408				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
409					Jobs: atc.JobConfigs{
410						{Name: "job-name"},
411					},
412				}, db.ConfigVersion(1), false)
413				Expect(err).ToNot(HaveOccurred())
414
415				var found bool
416				job1, found, err = pipeline1.Job("job-name")
417				Expect(err).ToNot(HaveOccurred())
418				Expect(found).To(BeTrue())
419
420				err = job1.RequestSchedule()
421				Expect(err).ToNot(HaveOccurred())
422
423				found, err = job1.Reload()
424				Expect(err).ToNot(HaveOccurred())
425				Expect(found).To(BeTrue())
426
427				err = job1.UpdateLastScheduled(job1.ScheduleRequestedTime())
428				Expect(err).ToNot(HaveOccurred())
429			})
430
431			It("does not fetch that job", func() {
432				jobs, err := jobFactory.JobsToSchedule()
433				Expect(err).ToNot(HaveOccurred())
434				Expect(len(jobs)).To(Equal(0))
435			})
436		})
437
438		Context("when there are multiple jobs with different times", func() {
439			BeforeEach(func() {
440				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
441					Jobs: atc.JobConfigs{
442						{Name: "job-name"},
443					},
444				}, db.ConfigVersion(1), false)
445				Expect(err).ToNot(HaveOccurred())
446
447				var found bool
448				job1, found, err = pipeline1.Job("job-name")
449				Expect(err).ToNot(HaveOccurred())
450				Expect(found).To(BeTrue())
451
452				err = job1.RequestSchedule()
453				Expect(err).ToNot(HaveOccurred())
454
455				team, err := teamFactory.CreateTeam(atc.Team{Name: "some-team"})
456				Expect(err).ToNot(HaveOccurred())
457
458				pipeline2, _, err := team.SavePipeline("fake-pipeline-two", atc.Config{
459					Jobs: atc.JobConfigs{
460						{Name: "job-fake"},
461					},
462				}, db.ConfigVersion(1), false)
463				Expect(err).ToNot(HaveOccurred())
464
465				job2, found, err = pipeline2.Job("job-fake")
466				Expect(err).ToNot(HaveOccurred())
467				Expect(found).To(BeTrue())
468
469				pipeline3, _, err := team.SavePipeline("fake-pipeline-three", atc.Config{
470					Jobs: atc.JobConfigs{
471						{Name: "job-fake-two"},
472					},
473				}, db.ConfigVersion(1), false)
474				Expect(err).ToNot(HaveOccurred())
475
476				job3, found, err = pipeline3.Job("job-fake-two")
477				Expect(err).ToNot(HaveOccurred())
478				Expect(found).To(BeTrue())
479
480				_, err = dbConn.Exec("UPDATE jobs SET last_scheduled = now() WHERE id = $1;", job2.ID())
481				Expect(err).ToNot(HaveOccurred())
482
483				err = job3.RequestSchedule()
484				Expect(err).ToNot(HaveOccurred())
485			})
486
487			It("fetches the jobs that have a requested schedule time later than it's last scheduled", func() {
488				jobs, err := jobFactory.JobsToSchedule()
489				Expect(err).ToNot(HaveOccurred())
490				Expect(len(jobs)).To(Equal(2))
491				jobNames := []string{jobs[0].Name(), jobs[1].Name()}
492				Expect(jobNames).To(ConsistOf(job1.Name(), job3.Name()))
493			})
494		})
495
496		Context("when the job is paused but has a later schedule requested time", func() {
497			BeforeEach(func() {
498				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
499					Jobs: atc.JobConfigs{
500						{Name: "job-name"},
501					},
502				}, db.ConfigVersion(1), false)
503				Expect(err).ToNot(HaveOccurred())
504
505				var found bool
506				job1, found, err = pipeline1.Job("job-name")
507				Expect(err).ToNot(HaveOccurred())
508				Expect(found).To(BeTrue())
509
510				err = job1.RequestSchedule()
511				Expect(err).ToNot(HaveOccurred())
512
513				err = job1.Pause()
514				Expect(err).ToNot(HaveOccurred())
515			})
516
517			It("does not fetch that job", func() {
518				jobs, err := jobFactory.JobsToSchedule()
519				Expect(err).ToNot(HaveOccurred())
520				Expect(len(jobs)).To(Equal(0))
521			})
522		})
523
524		Context("when the job is inactive but has a later schedule requested time", func() {
525			BeforeEach(func() {
526				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
527					Jobs: atc.JobConfigs{
528						{Name: "job-name"},
529					},
530				}, db.ConfigVersion(1), false)
531				Expect(err).ToNot(HaveOccurred())
532
533				var found bool
534				job1, found, err = pipeline1.Job("job-name")
535				Expect(err).ToNot(HaveOccurred())
536				Expect(found).To(BeTrue())
537
538				err = job1.RequestSchedule()
539				Expect(err).ToNot(HaveOccurred())
540
541				_, _, err = defaultTeam.SavePipeline("fake-pipeline", atc.Config{}, pipeline1.ConfigVersion(), false)
542				Expect(err).ToNot(HaveOccurred())
543			})
544
545			It("does not fetch that job", func() {
546				jobs, err := jobFactory.JobsToSchedule()
547				Expect(err).ToNot(HaveOccurred())
548				Expect(len(jobs)).To(Equal(0))
549			})
550		})
551
552		Context("when the pipeline is paused but it's job has a later schedule requested time", func() {
553			BeforeEach(func() {
554				pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
555					Jobs: atc.JobConfigs{
556						{Name: "job-name"},
557					},
558				}, db.ConfigVersion(1), false)
559				Expect(err).ToNot(HaveOccurred())
560
561				var found bool
562				job1, found, err = pipeline1.Job("job-name")
563				Expect(err).ToNot(HaveOccurred())
564				Expect(found).To(BeTrue())
565
566				err = job1.RequestSchedule()
567				Expect(err).ToNot(HaveOccurred())
568
569				err = pipeline1.Pause()
570				Expect(err).ToNot(HaveOccurred())
571			})
572
573			It("does not fetch that job", func() {
574				jobs, err := jobFactory.JobsToSchedule()
575				Expect(err).ToNot(HaveOccurred())
576				Expect(len(jobs)).To(Equal(0))
577			})
578		})
579
580		Describe("scheduler jobs resources", func() {
581			Context("when the job needed to be schedule has no resources", func() {
582				BeforeEach(func() {
583					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
584						Jobs: atc.JobConfigs{
585							{Name: "job-name"},
586						},
587					}, db.ConfigVersion(1), false)
588					Expect(err).ToNot(HaveOccurred())
589
590					var found bool
591					job1, found, err = pipeline1.Job("job-name")
592					Expect(err).ToNot(HaveOccurred())
593					Expect(found).To(BeTrue())
594
595					err = job1.RequestSchedule()
596					Expect(err).ToNot(HaveOccurred())
597				})
598
599				It("fetches that job and no resources", func() {
600					jobs, err := jobFactory.JobsToSchedule()
601					Expect(err).ToNot(HaveOccurred())
602					Expect(len(jobs)).To(Equal(1))
603					Expect(jobs[0].Name()).To(Equal(job1.Name()))
604					Expect(jobs[0].Resources).To(BeNil())
605				})
606			})
607
608			Context("when the job needed to be schedule uses resources", func() {
609				BeforeEach(func() {
610					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
611						Jobs: atc.JobConfigs{
612							{
613								Name: "job-name",
614								PlanSequence: []atc.Step{
615									{
616										Config: &atc.GetStep{
617											Name: "some-resource",
618										},
619									},
620								},
621							},
622						},
623
624						Resources: atc.ResourceConfigs{
625							{
626								Name: "some-resource",
627								Type: "some-type",
628								Source: atc.Source{
629									"some": "source",
630								},
631							},
632							{
633								Name: "unused-resource",
634							},
635						},
636					}, db.ConfigVersion(1), false)
637					Expect(err).ToNot(HaveOccurred())
638
639					var found bool
640					job1, found, err = pipeline1.Job("job-name")
641					Expect(err).ToNot(HaveOccurred())
642					Expect(found).To(BeTrue())
643
644					err = job1.RequestSchedule()
645					Expect(err).ToNot(HaveOccurred())
646				})
647
648				It("fetches that job and the used resource", func() {
649					jobs, err := jobFactory.JobsToSchedule()
650					Expect(err).ToNot(HaveOccurred())
651					Expect(len(jobs)).To(Equal(1))
652					Expect(jobs[0].Name()).To(Equal(job1.Name()))
653					Expect(jobs[0].Resources).To(HaveLen(1))
654					Expect(jobs[0].Resources).To(ConsistOf(
655						db.SchedulerResource{
656							Name:   "some-resource",
657							Type:   "some-type",
658							Source: atc.Source{"some": "source"},
659						},
660					))
661				})
662			})
663
664			Context("when multiple jobs needed to be schedule uses resources", func() {
665				BeforeEach(func() {
666					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
667						Jobs: atc.JobConfigs{
668							{
669								Name: "job-1",
670								PlanSequence: []atc.Step{
671									{
672										Config: &atc.GetStep{
673											Name: "some-resource",
674										},
675									},
676								},
677							},
678							{
679								Name: "job-2",
680								PlanSequence: []atc.Step{
681									{
682										Config: &atc.GetStep{
683											Name: "some-resource",
684										},
685									},
686									{
687										Config: &atc.GetStep{
688											Name: "other-resource",
689										},
690									},
691								},
692							},
693						},
694
695						Resources: atc.ResourceConfigs{
696							{
697								Name: "some-resource",
698								Type: "some-type",
699							},
700							{
701								Name: "other-resource",
702								Type: "some-type",
703							},
704							{
705								Name: "unused-resource",
706								Type: "some-type",
707							},
708						},
709					}, db.ConfigVersion(1), false)
710					Expect(err).ToNot(HaveOccurred())
711
712					pipeline2, _, err := defaultTeam.SavePipeline("fake-pipeline-2", atc.Config{
713						Jobs: atc.JobConfigs{
714							{
715								Name: "job-3",
716								PlanSequence: []atc.Step{
717									{
718										Config: &atc.GetStep{
719											Name: "some-resource",
720										},
721									},
722									{
723										Config: &atc.GetStep{
724											Name: "some-resource-2",
725										},
726									},
727								},
728							},
729						},
730
731						Resources: atc.ResourceConfigs{
732							{
733								Name: "some-resource",
734								Type: "other-type",
735							},
736							{
737								Name: "some-resource-2",
738								Type: "other-type",
739							},
740						},
741					}, db.ConfigVersion(1), false)
742					Expect(err).ToNot(HaveOccurred())
743
744					var found bool
745					job1, found, err = pipeline1.Job("job-1")
746					Expect(err).ToNot(HaveOccurred())
747					Expect(found).To(BeTrue())
748
749					job2, found, err = pipeline1.Job("job-2")
750					Expect(err).ToNot(HaveOccurred())
751					Expect(found).To(BeTrue())
752
753					job3, found, err = pipeline2.Job("job-3")
754					Expect(err).ToNot(HaveOccurred())
755					Expect(found).To(BeTrue())
756
757					err = job1.RequestSchedule()
758					Expect(err).ToNot(HaveOccurred())
759
760					err = job2.RequestSchedule()
761					Expect(err).ToNot(HaveOccurred())
762
763					err = job3.RequestSchedule()
764					Expect(err).ToNot(HaveOccurred())
765				})
766
767				It("fetches that job and the used resource", func() {
768					jobs, err := jobFactory.JobsToSchedule()
769					Expect(err).ToNot(HaveOccurred())
770
771					jobResources := make(map[string]db.SchedulerResources)
772					for _, job := range jobs {
773						jobResources[job.Name()] = job.Resources
774					}
775
776					Expect(jobResources).To(MatchAllKeys(Keys{
777						job1.Name(): ConsistOf(
778							db.SchedulerResource{
779								Name: "some-resource",
780								Type: "some-type",
781							},
782						),
783						job2.Name(): ConsistOf(
784							db.SchedulerResource{
785								Name: "some-resource",
786								Type: "some-type",
787							},
788							db.SchedulerResource{
789								Name: "other-resource",
790								Type: "some-type",
791							}),
792						job3.Name(): ConsistOf(
793							db.SchedulerResource{
794								Name: "some-resource",
795								Type: "other-type",
796							},
797							db.SchedulerResource{
798								Name: "some-resource-2",
799								Type: "other-type",
800							}),
801					}))
802				})
803			})
804
805			Context("when the job needed to be schedule uses resources as puts", func() {
806				BeforeEach(func() {
807					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
808						Jobs: atc.JobConfigs{
809							{
810								Name: "job-name",
811								PlanSequence: []atc.Step{
812									{
813										Config: &atc.PutStep{
814											Name: "some-resource",
815										},
816									},
817								},
818							},
819						},
820
821						Resources: atc.ResourceConfigs{
822							{
823								Name: "some-resource",
824								Type: "some-type",
825								Source: atc.Source{
826									"some": "source",
827								},
828							},
829							{
830								Name: "unused-resource",
831							},
832						},
833					}, db.ConfigVersion(1), false)
834					Expect(err).ToNot(HaveOccurred())
835
836					var found bool
837					job1, found, err = pipeline1.Job("job-name")
838					Expect(err).ToNot(HaveOccurred())
839					Expect(found).To(BeTrue())
840
841					err = job1.RequestSchedule()
842					Expect(err).ToNot(HaveOccurred())
843				})
844
845				It("fetches that job and the used resource", func() {
846					jobs, err := jobFactory.JobsToSchedule()
847					Expect(err).ToNot(HaveOccurred())
848					Expect(len(jobs)).To(Equal(1))
849					Expect(jobs[0].Name()).To(Equal(job1.Name()))
850					Expect(jobs[0].Resources).To(ConsistOf(
851						db.SchedulerResource{
852							Name:   "some-resource",
853							Type:   "some-type",
854							Source: atc.Source{"some": "source"},
855						},
856					))
857				})
858			})
859
860			Context("when the job needed to be schedule uses the resource as a put and a get", func() {
861				BeforeEach(func() {
862					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
863						Jobs: atc.JobConfigs{
864							{
865								Name: "job-name",
866								PlanSequence: []atc.Step{
867									{
868										Config: &atc.GetStep{
869											Name: "some-resource",
870										},
871									},
872									{
873										Config: &atc.PutStep{
874											Name: "some-resource",
875										},
876									},
877								},
878							},
879						},
880
881						Resources: atc.ResourceConfigs{
882							{
883								Name: "some-resource",
884								Type: "some-type",
885								Source: atc.Source{
886									"some": "source",
887								},
888							},
889							{
890								Name: "unused-resource",
891							},
892						},
893					}, db.ConfigVersion(1), false)
894					Expect(err).ToNot(HaveOccurred())
895
896					var found bool
897					job1, found, err = pipeline1.Job("job-name")
898					Expect(err).ToNot(HaveOccurred())
899					Expect(found).To(BeTrue())
900
901					err = job1.RequestSchedule()
902					Expect(err).ToNot(HaveOccurred())
903				})
904
905				It("fetches that job and the used resource", func() {
906					jobs, err := jobFactory.JobsToSchedule()
907					Expect(err).ToNot(HaveOccurred())
908					Expect(len(jobs)).To(Equal(1))
909					Expect(jobs[0].Name()).To(Equal(job1.Name()))
910					Expect(jobs[0].Resources).To(ConsistOf(
911						db.SchedulerResource{
912							Name:   "some-resource",
913							Type:   "some-type",
914							Source: atc.Source{"some": "source"},
915						},
916					))
917				})
918			})
919		})
920
921		Describe("schedule jobs resource types", func() {
922			Context("when the pipeline for the job needed to be scheduled uses custom resource types", func() {
923				BeforeEach(func() {
924					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
925						Jobs: atc.JobConfigs{
926							{Name: "job-name"},
927						},
928						ResourceTypes: atc.ResourceTypes{
929							{
930								Name: "some-type",
931								Type: "other-type",
932							},
933						},
934					}, db.ConfigVersion(1), false)
935					Expect(err).ToNot(HaveOccurred())
936
937					var found bool
938					job1, found, err = pipeline1.Job("job-name")
939					Expect(err).ToNot(HaveOccurred())
940					Expect(found).To(BeTrue())
941
942					err = job1.RequestSchedule()
943					Expect(err).ToNot(HaveOccurred())
944				})
945
946				It("fetches that job and resource type", func() {
947					jobs, err := jobFactory.JobsToSchedule()
948					Expect(err).ToNot(HaveOccurred())
949					Expect(len(jobs)).To(Equal(1))
950					Expect(jobs[0].Name()).To(Equal(job1.Name()))
951					Expect(jobs[0].ResourceTypes).To(ConsistOf(
952						atc.VersionedResourceType{
953							ResourceType: atc.ResourceType{
954								Name: "some-type",
955								Type: "other-type",
956							},
957						},
958					))
959				})
960			})
961
962			Context("when multiple job from different pipelines uses custom resource types", func() {
963				BeforeEach(func() {
964					pipeline1, _, err := defaultTeam.SavePipeline("fake-pipeline", atc.Config{
965						Jobs: atc.JobConfigs{
966							{Name: "job-1"},
967							{Name: "job-2"},
968						},
969						ResourceTypes: atc.ResourceTypes{
970							{
971								Name: "some-type",
972								Type: "other-type",
973							},
974						},
975					}, db.ConfigVersion(1), false)
976					Expect(err).ToNot(HaveOccurred())
977
978					pipeline2, _, err := defaultTeam.SavePipeline("fake-pipeline-2", atc.Config{
979						Jobs: atc.JobConfigs{
980							{Name: "job-3"},
981						},
982						ResourceTypes: atc.ResourceTypes{
983							{
984								Name: "some-type-1",
985								Type: "other-type-1",
986							},
987							{
988								Name: "some-type-2",
989								Type: "other-type-2",
990							},
991						},
992					}, db.ConfigVersion(1), false)
993					Expect(err).ToNot(HaveOccurred())
994
995					var found bool
996					job1, found, err = pipeline1.Job("job-1")
997					Expect(err).ToNot(HaveOccurred())
998					Expect(found).To(BeTrue())
999
1000					err = job1.RequestSchedule()
1001					Expect(err).ToNot(HaveOccurred())
1002
1003					job2, found, err = pipeline1.Job("job-2")
1004					Expect(err).ToNot(HaveOccurred())
1005					Expect(found).To(BeTrue())
1006
1007					err = job2.RequestSchedule()
1008					Expect(err).ToNot(HaveOccurred())
1009
1010					job3, found, err = pipeline2.Job("job-3")
1011					Expect(err).ToNot(HaveOccurred())
1012					Expect(found).To(BeTrue())
1013
1014					err = job3.RequestSchedule()
1015					Expect(err).ToNot(HaveOccurred())
1016				})
1017
1018				It("fetches all jobs and resource types", func() {
1019					jobs, err := jobFactory.JobsToSchedule()
1020					Expect(err).ToNot(HaveOccurred())
1021
1022					jobResourceTypes := make(map[string]atc.VersionedResourceTypes)
1023					for _, job := range jobs {
1024						jobResourceTypes[job.Name()] = job.ResourceTypes
1025					}
1026
1027					Expect(jobResourceTypes).To(MatchAllKeys(Keys{
1028						job1.Name(): ConsistOf(
1029							atc.VersionedResourceType{
1030								ResourceType: atc.ResourceType{
1031									Name: "some-type",
1032									Type: "other-type",
1033								},
1034							},
1035						),
1036						job2.Name(): ConsistOf(
1037							atc.VersionedResourceType{
1038								ResourceType: atc.ResourceType{
1039									Name: "some-type",
1040									Type: "other-type",
1041								},
1042							},
1043						),
1044						job3.Name(): ConsistOf(
1045							atc.VersionedResourceType{
1046								ResourceType: atc.ResourceType{
1047									Name: "some-type-1",
1048									Type: "other-type-1",
1049								},
1050							},
1051							atc.VersionedResourceType{
1052								ResourceType: atc.ResourceType{
1053									Name: "some-type-2",
1054									Type: "other-type-2",
1055								},
1056							},
1057						),
1058					}))
1059				})
1060			})
1061		})
1062	})
1063})
1064
1065var _ = Context("SchedulerResource", func() {
1066	var resource db.SchedulerResource
1067
1068	BeforeEach(func() {
1069		resource = db.SchedulerResource{
1070			Name: "some-name",
1071			Type: "some-type",
1072			Source: atc.Source{
1073				"some-key": "some-value",
1074			},
1075		}
1076	})
1077
1078	Context("ApplySourceDefaults", func() {
1079		var resourceTypes atc.VersionedResourceTypes
1080
1081		BeforeEach(func() {
1082			resourceTypes = atc.VersionedResourceTypes{
1083				{
1084					ResourceType: atc.ResourceType{
1085						Name:     "some-type",
1086						Defaults: atc.Source{"default-key": "default-value"},
1087					},
1088				},
1089			}
1090		})
1091
1092		JustBeforeEach(func() {
1093			resource.ApplySourceDefaults(resourceTypes)
1094		})
1095
1096		It("should apply defaults", func() {
1097			Expect(resource).To(Equal(db.SchedulerResource{
1098				Name: "some-name",
1099				Type: "some-type",
1100				Source: atc.Source{
1101					"some-key":    "some-value",
1102					"default-key": "default-value",
1103				},
1104			}))
1105		})
1106
1107		Context("when the parent resource is not found", func() {
1108			BeforeEach(func() {
1109				resourceTypes = atc.VersionedResourceTypes{}
1110				atc.LoadBaseResourceTypeDefaults(map[string]atc.Source{
1111					"some-type": {"default-key": "default-value"},
1112				})
1113			})
1114
1115			AfterEach(func() {
1116				atc.LoadBaseResourceTypeDefaults(map[string]atc.Source{})
1117			})
1118
1119			It("should apply defaults using the base resource type", func() {
1120				Expect(resource).To(Equal(db.SchedulerResource{
1121					Name: "some-name",
1122					Type: "some-type",
1123					Source: atc.Source{
1124						"some-key":    "some-value",
1125						"default-key": "default-value",
1126					},
1127				}))
1128			})
1129		})
1130	})
1131})
1132