github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/scheduler/job_scheduling_test.go (about) 1 package scheduler_test 2 3 import ( 4 "errors" 5 "fmt" 6 7 "code.cloudfoundry.org/lager" 8 "github.com/pf-qiu/concourse/v6/atc" 9 "github.com/pf-qiu/concourse/v6/atc/db" 10 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 11 "github.com/pf-qiu/concourse/v6/atc/scheduler" 12 "github.com/pf-qiu/concourse/v6/atc/scheduler/schedulerfakes" 13 . "github.com/onsi/ginkgo/extensions/table" 14 . "github.com/onsi/gomega" 15 ) 16 17 var _ = DescribeTable("Job Scheduling", 18 (Example).Run, 19 20 Entry("one pending build that can be successfully started", Example{ 21 Job: DBJob{ 22 Builds: []DBBuild{ 23 {ID: 1}, 24 }, 25 }, 26 27 Result: Result{ 28 StartedBuilds: []int{1}, 29 NeedsRetry: false, 30 }, 31 }), 32 33 Entry("one pending build that is aborted", Example{ 34 Job: DBJob{ 35 Builds: []DBBuild{ 36 {ID: 1, Aborted: true}, 37 }, 38 }, 39 40 Result: Result{ 41 StartedBuilds: []int{}, 42 NeedsRetry: false, 43 }, 44 }), 45 46 Entry("one pending build that has reached max in flight", Example{ 47 Job: DBJob{ 48 Builds: []DBBuild{ 49 {ID: 1, MaxInFlightReached: true}, 50 }, 51 }, 52 53 Result: Result{ 54 StartedBuilds: []int{}, 55 NeedsRetry: true, 56 }, 57 }), 58 59 Entry("one manually triggered pending build that does not have resources checked", Example{ 60 Job: DBJob{ 61 Builds: []DBBuild{ 62 {ID: 1, ManuallyTriggered: true, ResourcesNotChecked: true}, 63 }, 64 }, 65 66 Result: Result{ 67 StartedBuilds: []int{}, 68 NeedsRetry: true, 69 }, 70 }), 71 72 Entry("one pending build that does not have inputs determined", Example{ 73 Job: DBJob{ 74 Builds: []DBBuild{ 75 {ID: 1, InputsNotDetermined: true}, 76 }, 77 }, 78 79 Result: Result{ 80 StartedBuilds: []int{}, 81 NeedsRetry: false, 82 }, 83 }), 84 85 Entry("one pending build that cannot create build plan", Example{ 86 Job: DBJob{ 87 Builds: []DBBuild{ 88 {ID: 1, CreatingBuildPlanFails: true}, 89 }, 90 }, 91 92 Result: Result{ 93 StartedBuilds: []int{}, 94 NeedsRetry: false, 95 }, 96 }), 97 98 Entry("one pending build that is unable to start", Example{ 99 Job: DBJob{ 100 Builds: []DBBuild{ 101 {ID: 1, UnableToStart: true}, 102 }, 103 }, 104 105 Result: Result{ 106 StartedBuilds: []int{}, 107 NeedsRetry: false, 108 }, 109 }), 110 111 Entry("one scheduler build, one manually triggered build and one rerun build", Example{ 112 Job: DBJob{ 113 Builds: []DBBuild{ 114 {ID: 4, RerunOfBuildID: 1}, 115 {ID: 2}, 116 {ID: 3, ManuallyTriggered: true}, 117 }, 118 }, 119 120 Result: Result{ 121 StartedBuilds: []int{4, 2, 3}, 122 NeedsRetry: false, 123 }, 124 }), 125 126 Entry("if pending builds is aborted, next build will continue to schedule", Example{ 127 Job: DBJob{ 128 Builds: []DBBuild{ 129 {ID: 1, Aborted: true}, 130 {ID: 2}, 131 }, 132 }, 133 134 Result: Result{ 135 StartedBuilds: []int{2}, 136 NeedsRetry: false, 137 }, 138 }), 139 140 Entry("if max in flight is reached, next builds will not schedule", Example{ 141 Job: DBJob{ 142 Builds: []DBBuild{ 143 {ID: 1, MaxInFlightReached: true}, 144 {ID: 2}, 145 }, 146 }, 147 148 Result: Result{ 149 StartedBuilds: []int{}, 150 NeedsRetry: true, 151 }, 152 }), 153 154 Entry("if resources have not checked for a manually triggered build, next builds will not schedule", Example{ 155 Job: DBJob{ 156 Builds: []DBBuild{ 157 {ID: 1, ManuallyTriggered: true, ResourcesNotChecked: true}, 158 {ID: 2}, 159 }, 160 }, 161 162 Result: Result{ 163 StartedBuilds: []int{}, 164 NeedsRetry: true, 165 }, 166 }), 167 168 Entry("if the rerun build has no inputs determined, the normal build will continue to get scheduled", Example{ 169 Job: DBJob{ 170 Builds: []DBBuild{ 171 {ID: 3, RerunOfBuildID: 1, InputsNotDetermined: true}, 172 {ID: 2}, 173 }, 174 }, 175 176 Result: Result{ 177 StartedBuilds: []int{2}, 178 NeedsRetry: false, 179 }, 180 }), 181 182 Entry("if inputs are not determined on a regular build, next builds will not schedule", Example{ 183 Job: DBJob{ 184 Builds: []DBBuild{ 185 {ID: 1, InputsNotDetermined: true}, 186 {ID: 2}, 187 }, 188 }, 189 190 Result: Result{ 191 StartedBuilds: []int{}, 192 NeedsRetry: false, 193 }, 194 }), 195 196 Entry("if both rerun builds cannot determine inputs, next build will continue to schedule", Example{ 197 Job: DBJob{ 198 Builds: []DBBuild{ 199 {ID: 4, RerunOfBuildID: 1, InputsNotDetermined: true}, 200 {ID: 3, RerunOfBuildID: 1, InputsNotDetermined: true}, 201 {ID: 2}, 202 }, 203 }, 204 205 Result: Result{ 206 StartedBuilds: []int{2}, 207 NeedsRetry: false, 208 }, 209 }), 210 211 Entry("if regular build fails to schedule, next rerun build will not schedule", Example{ 212 Job: DBJob{ 213 Builds: []DBBuild{ 214 {ID: 2, InputsNotDetermined: true}, 215 {ID: 4, RerunOfBuildID: 3}, 216 }, 217 }, 218 219 Result: Result{ 220 StartedBuilds: []int{}, 221 NeedsRetry: false, 222 }, 223 }), 224 ) 225 226 type Example struct { 227 Job DBJob 228 Result Result 229 } 230 231 type DBJob struct { 232 Paused bool 233 PipelinePaused bool 234 235 Builds []DBBuild 236 } 237 238 type DBBuild struct { 239 ID int 240 RerunOfBuildID int 241 ManuallyTriggered bool 242 243 Aborted bool 244 245 InputsNotDetermined bool 246 ResourcesNotChecked bool 247 MaxInFlightReached bool 248 249 CreatingBuildPlanFails bool 250 UnableToStart bool 251 } 252 253 type Result struct { 254 StartedBuilds []int 255 NeedsRetry bool 256 Errored bool 257 } 258 259 func (example Example) Run() { 260 fakePlanner := new(schedulerfakes.FakeBuildPlanner) 261 fakeAlgorithm := new(schedulerfakes.FakeAlgorithm) 262 fakeAlgorithm.ComputeReturns(nil, true, false, nil) 263 264 buildStarter := scheduler.NewBuildStarter(fakePlanner, fakeAlgorithm) 265 266 fakeJob := new(dbfakes.FakeJob) 267 fakeJob.ConfigReturns(atc.JobConfig{}, nil) 268 fakeJob.SaveNextInputMappingReturns(nil) 269 270 var expectedScheduledBuilds []*dbfakes.FakeBuild 271 var pendingBuilds []db.Build 272 for i, build := range example.Job.Builds { 273 fakeBuild := new(dbfakes.FakeBuild) 274 fakeBuild.IDReturns(build.ID) 275 fakeBuild.NameReturns(fmt.Sprint(build.ID)) 276 fakeBuild.IsAbortedReturns(build.Aborted) 277 fakeBuild.RerunOfReturns(build.RerunOfBuildID) 278 fakeBuild.IsManuallyTriggeredReturns(build.ManuallyTriggered) 279 fakeBuild.FinishReturns(nil) 280 281 if build.MaxInFlightReached { 282 fakeJob.ScheduleBuildReturnsOnCall(i, false, nil) 283 } else { 284 fakeJob.ScheduleBuildReturnsOnCall(i, true, nil) 285 } 286 287 if build.ResourcesNotChecked { 288 fakeBuild.ResourcesCheckedReturns(false, nil) 289 } else { 290 fakeBuild.ResourcesCheckedReturns(true, nil) 291 } 292 293 if build.InputsNotDetermined { 294 fakeBuild.AdoptInputsAndPipesReturns(nil, false, nil) 295 fakeBuild.AdoptRerunInputsAndPipesReturns(nil, false, nil) 296 } else { 297 fakeBuild.AdoptInputsAndPipesReturns(nil, true, nil) 298 fakeBuild.AdoptRerunInputsAndPipesReturns(nil, true, nil) 299 } 300 301 if build.CreatingBuildPlanFails { 302 fakePlanner.CreateReturns(atc.Plan{}, errors.New("disaster")) 303 } else { 304 fakePlanner.CreateReturns(atc.Plan{}, nil) 305 } 306 307 if build.UnableToStart { 308 fakeBuild.StartReturns(false, nil) 309 } else { 310 fakeBuild.StartReturns(true, nil) 311 } 312 313 expectedScheduledBuilds = append(expectedScheduledBuilds, fakeBuild) 314 pendingBuilds = append(pendingBuilds, fakeBuild) 315 } 316 317 fakeJob.GetPendingBuildsReturns(pendingBuilds, nil) 318 319 jobInputs := db.InputConfigs{ 320 { 321 Name: "fake-resource", 322 }, 323 } 324 325 needsRetry, err := buildStarter.TryStartPendingBuildsForJob(lager.NewLogger("job-scheduling-tests"), db.SchedulerJob{ 326 Job: fakeJob, 327 Resources: db.SchedulerResources{ 328 { 329 Name: "fake-resource", 330 Type: "fake-resource-type", 331 Source: atc.Source{ 332 "some": "source", 333 }, 334 }, 335 }, 336 }, 337 jobInputs) 338 if err != nil { 339 Expect(example.Result.Errored).To(BeTrue()) 340 } else { 341 Expect(example.Result.Errored).To(BeFalse()) 342 Expect(needsRetry).To(Equal(example.Result.NeedsRetry)) 343 for i, buildID := range example.Result.StartedBuilds { 344 if expectedScheduledBuilds[i].StartCallCount() > 0 { 345 Expect(expectedScheduledBuilds[i].ID()).To(Equal(buildID)) 346 Expect(expectedScheduledBuilds[i].StartCallCount()).To(Equal(1)) 347 } 348 } 349 } 350 }