func (mr *MultiRunner) requestBuild(runner *common.RunnerConfig) *common.Build { if runner == nil { return nil } if !mr.isHealthy(runner) { return nil } count := mr.buildsForRunner(runner) limit := helpers.NonZeroOrDefault(runner.Limit, math.MaxInt32) if count >= limit { return nil } buildData, healthy := common.GetBuild(*runner) if healthy { mr.makeHealthy(runner) } else { mr.makeUnhealthy(runner) } if buildData == nil { return nil } mr.debugln("Received new build for", runner.ShortDescription(), "build", buildData.ID) newBuild := &common.Build{ GetBuildResponse: *buildData, Runner: runner, BuildAbort: mr.abortBuilds, } return newBuild }
func (mr *MultiRunner) makeUnhealthy(runner *common.RunnerConfig) { health := mr.getHealth(runner) health.failures++ if health.failures >= common.HealthyChecks { mr.errorln("Runner", runner.ShortDescription(), "is not healthy and will be disabled!") } }
func (mr *MultiRunner) isHealthy(runner *common.RunnerConfig) bool { health := mr.getHealth(runner) if health.failures < common.HealthyChecks { return true } if time.Since(health.lastCheck) > common.HealthCheckInterval*time.Second { mr.errorln("Runner", runner.ShortDescription(), "is not healthy, but will be checked!") health.failures = 0 health.lastCheck = time.Now() return true } return false }
func runSingle(c *cli.Context) { buildsDir := c.String("builds-dir") shell := c.String("shell") config := common.NewConfig() runner := common.RunnerConfig{ URL: c.String("url"), Token: c.String("token"), Executor: c.String("executor"), BuildsDir: &buildsDir, Shell: &shell, } if len(runner.URL) == 0 { log.Fatalln("Missing URL") } if len(runner.Token) == 0 { log.Fatalln("Missing Token") } if len(runner.Executor) == 0 { log.Fatalln("Missing Executor") } go runServer(c.String("addr")) go runHerokuURL(c.String("heroku-url")) signals := make(chan os.Signal) signal.Notify(signals, os.Interrupt, syscall.SIGTERM) log.Println("Starting runner for", runner.URL, "with token", runner.ShortDescription(), "...") finished := false abortSignal := make(chan os.Signal) doneSignal := make(chan int, 1) go func() { interrupt := <-signals log.Warningln("Requested exit:", interrupt) finished = true go func() { for { abortSignal <- interrupt } }() select { case newSignal := <-signals: log.Fatalln("forced exit:", newSignal) case <-time.After(common.ShutdownTimeout * time.Second): log.Fatalln("shutdown timedout") case <-doneSignal: } }() for !finished { buildData, healthy := common.GetBuild(runner) if !healthy { log.Println("Runner is not healthy!") select { case <-time.After(common.NotHealthyCheckInterval * time.Second): case <-abortSignal: } continue } if buildData == nil { select { case <-time.After(common.CheckInterval * time.Second): case <-abortSignal: } continue } newBuild := common.Build{ GetBuildResponse: *buildData, Runner: &runner, BuildAbort: abortSignal, } newBuild.AssignID() newBuild.Run(config) } doneSignal <- 0 }