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  }