func Job(dbJob db.SavedJob, job atc.JobConfig, groups atc.GroupConfigs, finishedBuild, nextBuild *db.Build) atc.Job { generator := rata.NewRequestGenerator("", routes.Routes) req, err := generator.CreateRequest( routes.GetJob, rata.Params{"job": job.Name, "pipeline_name": dbJob.PipelineName}, nil, ) if err != nil { panic("failed to generate url: " + err.Error()) } var presentedNextBuild, presentedFinishedBuild *atc.Build if nextBuild != nil { presented := Build(*nextBuild) presentedNextBuild = &presented } if finishedBuild != nil { presented := Build(*finishedBuild) presentedFinishedBuild = &presented } groupNames := []string{} for _, group := range groups { for _, name := range group.Jobs { if name == job.Name { groupNames = append(groupNames, group.Name) } } } return atc.Job{ Name: job.Name, URL: req.URL.String(), Paused: dbJob.Paused, FinishedBuild: presentedFinishedBuild, NextBuild: presentedNextBuild, Inputs: job.Inputs(), Outputs: job.Outputs(), Groups: groupNames, } }
"github.com/concourse/atc/scheduler/fakes" "github.com/pivotal-golang/lager/lagertest" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Scheduler", func() { var ( fakePipelineDB *fakes.FakePipelineDB fakeBuildsDB *fakes.FakeBuildsDB factory *fakes.FakeBuildFactory fakeEngine *enginefakes.FakeEngine fakeScanner *fakes.FakeScanner createdPlan atc.Plan job atc.JobConfig resources atc.ResourceConfigs scheduler *Scheduler logger *lagertest.TestLogger ) BeforeEach(func() { fakePipelineDB = new(fakes.FakePipelineDB) fakeBuildsDB = new(fakes.FakeBuildsDB) factory = new(fakes.FakeBuildFactory) fakeEngine = new(enginefakes.FakeEngine) fakeScanner = new(fakes.FakeScanner)
Context("when two resource types have the same name", func() { BeforeEach(func() { config.ResourceTypes = append(config.ResourceTypes, config.ResourceTypes...) }) It("returns an error", func() { Expect(validateErr).To(HaveOccurred()) Expect(validateErr.Error()).To(ContainSubstring( "resource_types[0] and resource_types[1] have the same name ('some-resource-type')", )) }) }) }) Describe("validating a job", func() { var job atc.JobConfig BeforeEach(func() { job = atc.JobConfig{ Name: "some-other-job", } }) Context("when a job has no name", func() { BeforeEach(func() { job.Name = "" config.Jobs = append(config.Jobs, job) }) It("returns an error", func() { Expect(validateErr).To(HaveOccurred())
package config_test import ( "github.com/concourse/atc" "github.com/concourse/atc/config" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Job config", func() { Describe("JobInputs", func() { var ( jobConfig atc.JobConfig inputs []config.JobInput ) BeforeEach(func() { jobConfig = atc.JobConfig{} }) JustBeforeEach(func() { inputs = config.JobInputs(jobConfig) }) Context("with a build plan", func() { Context("with an empty plan", func() { BeforeEach(func() { jobConfig.Plan = atc.PlanSequence{} })
package factory_test import ( "github.com/concourse/atc" . "github.com/concourse/atc/scheduler/factory" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Factory", func() { var ( factory *BuildFactory job atc.JobConfig resources atc.ResourceConfigs expectedPlan atc.Plan ) BeforeEach(func() { factory = &BuildFactory{ PipelineName: "some-pipeline", } job = atc.JobConfig{ Name: "some-job", } expectedPlan = atc.Plan{ Compose: &atc.ComposePlan{
func (s *Scheduler) BuildLatestInputs(logger lager.Logger, job atc.JobConfig, resources atc.ResourceConfigs) error { logger = logger.Session("build-latest") inputs := job.Inputs() if len(inputs) == 0 { // no inputs; no-op return nil } latestInputs, err := s.PipelineDB.GetLatestInputVersions(job.Name, inputs) if err != nil { if err == db.ErrNoVersions { logger.Debug("no-input-versions-available") return nil } logger.Error("failed-to-get-latest-input-versions", err) return err } checkInputs := []db.BuildInput{} for _, input := range latestInputs { for _, ji := range inputs { if ji.Name == input.Name { if ji.Trigger { checkInputs = append(checkInputs, input) } break } } } if len(checkInputs) == 0 { logger.Debug("no-triggered-input-versions") return nil } existingBuild, err := s.PipelineDB.GetJobBuildForInputs(job.Name, checkInputs) if err == nil { logger.Debug("build-already-exists-for-inputs", lager.Data{ "existing-build": existingBuild.ID, }) return nil } build, created, err := s.PipelineDB.CreateJobBuildForCandidateInputs(job.Name) if err != nil { logger.Error("failed-to-create-build", err) return err } if !created { logger.Debug("waiting-for-existing-build-to-determine-inputs", lager.Data{ "existing-build": build.ID, }) return nil } logger.Debug("created-build", lager.Data{"build": build.ID}) // NOTE: this is intentionally serial within a scheduler tick, so that // multiple ATCs don't do redundant work to determine a build's inputs. s.scheduleAndResumePendingBuild(logger, build, job, resources) return nil }
func (s *Scheduler) scheduleAndResumePendingBuild(logger lager.Logger, build db.Build, job atc.JobConfig, resources atc.ResourceConfigs) engine.Build { logger = logger.WithData(lager.Data{"build": build.ID}) scheduled, err := s.PipelineDB.ScheduleBuild(build.ID, job) if err != nil { logger.Error("failed-to-schedule-build", err) return nil } if !scheduled { logger.Debug("build-could-not-be-scheduled") return nil } buildInputs := job.Inputs() for _, input := range buildInputs { scanLog := logger.Session("scan", lager.Data{ "input": input.Name, "resource": input.Resource, }) err := s.Scanner.Scan(scanLog, input.Resource) if err != nil { scanLog.Error("failed-to-scan", err) err := s.BuildsDB.ErrorBuild(build.ID, err) if err != nil { logger.Error("failed-to-mark-build-as-errored", err) } return nil } scanLog.Info("done") } inputs, err := s.PipelineDB.GetLatestInputVersions(job.Name, buildInputs) if err != nil { logger.Error("failed-to-get-latest-input-versions", err) return nil } err = s.PipelineDB.UseInputsForBuild(build.ID, inputs) if err != nil { logger.Error("failed-to-use-inputs-for-build", err) return nil } plan, err := s.Factory.Create(job, resources, inputs) if err != nil { logger.Error("failed-to-create-build-plan", err) return nil } createdBuild, err := s.Engine.CreateBuild(build, plan) if err != nil { logger.Error("failed-to-create-build", err) return nil } if createdBuild != nil { logger.Info("building") go createdBuild.Resume(logger) } return createdBuild }
Context("when two resources have the same name", func() { BeforeEach(func() { config.Resources = append(config.Resources, config.Resources...) }) It("returns an error", func() { Ω(validateErr).Should(HaveOccurred()) Ω(validateErr.Error()).Should(ContainSubstring( "resources[0] and resources[1] have the same name ('some-resource')", )) }) }) }) Describe("validating a job", func() { var job atc.JobConfig BeforeEach(func() { job = atc.JobConfig{ Name: "some-other-job", TaskConfigPath: "some-task-config", } }) Context("when a job has a only a name and a build config", func() { BeforeEach(func() { config.Jobs = append(config.Jobs, job) }) It("returns no error", func() { Ω(validateErr).ShouldNot(HaveOccurred())
. "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) var _ = Describe("Scheduler", func() { var ( fakePipelineDB *fakes.FakePipelineDB fakeBuildsDB *fakes.FakeBuildsDB factory *fakes.FakeBuildFactory fakeEngine *enginefakes.FakeEngine fakeScanner *fakes.FakeScanner lease *dbfakes.FakeLease createdPlan atc.Plan job atc.JobConfig resources atc.ResourceConfigs resourceTypes atc.ResourceTypes scheduler *Scheduler someVersions *algorithm.VersionsDB logger *lagertest.TestLogger ) BeforeEach(func() { fakePipelineDB = new(fakes.FakePipelineDB) fakeBuildsDB = new(fakes.FakeBuildsDB) factory = new(fakes.FakeBuildFactory)
func (s *Scheduler) BuildLatestInputs(logger lager.Logger, job atc.JobConfig, resources atc.ResourceConfigs) error { logger = logger.Session("build-latest") inputs := job.Inputs() if len(inputs) == 0 { // no inputs; no-op return nil } latestInputs, err := s.PipelineDB.GetLatestInputVersions(inputs) if err != nil { if err == db.ErrNoVersions { logger.Debug("no-input-versions-available") return nil } logger.Error("failed-to-get-latest-input-versions", err) return err } checkInputs := []db.BuildInput{} for _, input := range latestInputs { for _, ji := range inputs { if ji.Name == input.Name { if ji.Trigger { checkInputs = append(checkInputs, input) } break } } } if len(checkInputs) == 0 { logger.Debug("no-triggered-input-versions") return nil } existingBuild, err := s.PipelineDB.GetJobBuildForInputs(job.Name, checkInputs) if err == nil { logger.Debug("build-already-exists-for-inputs", lager.Data{ "existing-build": existingBuild.ID, }) return nil } build, created, err := s.PipelineDB.CreateJobBuildForCandidateInputs(job.Name) if err != nil { logger.Error("failed-to-create-build", err) return err } if !created { logger.Debug("did-not-create-build-as-it-already-is-pending") return nil } logger = logger.WithData(lager.Data{"build": build.ID}) logger.Debug("created-build") createdBuild := s.scheduleAndResumePendingBuild(logger, build, job, resources) if createdBuild != nil { logger.Info("building") go createdBuild.Resume(logger) } return nil }
fakeDB.GetJobReturns(db.SavedJob{}, errors.New("disaster")) _, err := scheduler.NewJobService(atc.JobConfig{}, fakeDB, fakeScanner) Expect(err).To(HaveOccurred()) }) }) }) Describe("CanBuildBeScheduled", func() { var ( service scheduler.JobService dbSavedJob db.SavedJob jobConfig atc.JobConfig logger *lagertest.TestLogger dbBuild db.Build buildPrep db.BuildPreparation someVersions *algorithm.VersionsDB canBuildBeScheduled bool reason string buildInputs []db.BuildInput err error ) BeforeEach(func() { fakeDB = new(fakes.FakeJobServiceDB) fakeScanner = new(fakes.FakeScanner) jobConfig = atc.JobConfig{ Name: "some-job",