github.com/secure-build/gitlab-runner@v12.5.0+incompatible/commands/single.go (about) 1 package commands 2 3 import ( 4 "os" 5 "os/signal" 6 "syscall" 7 "time" 8 9 "github.com/sirupsen/logrus" 10 "github.com/tevino/abool" 11 "github.com/urfave/cli" 12 13 "gitlab.com/gitlab-org/gitlab-runner/common" 14 "gitlab.com/gitlab-org/gitlab-runner/network" 15 ) 16 17 type RunSingleCommand struct { 18 common.RunnerConfig 19 network common.Network 20 WaitTimeout int `long:"wait-timeout" description:"How long to wait in seconds before receiving the first job"` 21 lastBuild time.Time 22 runForever bool 23 MaxBuilds int `long:"max-builds" description:"How many builds to process before exiting"` 24 finished *abool.AtomicBool 25 interruptSignals chan os.Signal 26 } 27 28 func waitForInterrupts(finished *abool.AtomicBool, abortSignal chan os.Signal, doneSignal chan int, interruptSignals chan os.Signal) { 29 if interruptSignals == nil { 30 interruptSignals = make(chan os.Signal) 31 } 32 signal.Notify(interruptSignals, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) 33 34 interrupt := <-interruptSignals 35 if finished != nil { 36 finished.Set() 37 } 38 39 // request stop, but wait for force exit 40 for interrupt == syscall.SIGQUIT { 41 logrus.Warningln("Requested quit, waiting for builds to finish") 42 interrupt = <-interruptSignals 43 } 44 45 logrus.Warningln("Requested exit:", interrupt) 46 47 go func() { 48 for { 49 abortSignal <- interrupt 50 } 51 }() 52 53 select { 54 case newSignal := <-interruptSignals: 55 logrus.Fatalln("forced exit:", newSignal) 56 case <-time.After(common.ShutdownTimeout * time.Second): 57 logrus.Fatalln("shutdown timed out") 58 case <-doneSignal: 59 } 60 } 61 62 // Things to do after a build 63 func (r *RunSingleCommand) postBuild() { 64 if r.MaxBuilds > 0 { 65 r.MaxBuilds-- 66 } 67 r.lastBuild = time.Now() 68 } 69 70 func (r *RunSingleCommand) processBuild(data common.ExecutorData, abortSignal chan os.Signal) (err error) { 71 jobData, healthy := r.network.RequestJob(r.RunnerConfig, nil) 72 if !healthy { 73 logrus.Println("Runner is not healthy!") 74 select { 75 case <-time.After(common.NotHealthyCheckInterval * time.Second): 76 case <-abortSignal: 77 } 78 return 79 } 80 81 if jobData == nil { 82 select { 83 case <-time.After(common.CheckInterval): 84 case <-abortSignal: 85 } 86 return 87 } 88 89 config := common.NewConfig() 90 newBuild, err := common.NewBuild(*jobData, &r.RunnerConfig, abortSignal, data) 91 if err != nil { 92 return 93 } 94 95 jobCredentials := &common.JobCredentials{ 96 ID: jobData.ID, 97 Token: jobData.Token, 98 } 99 trace, err := r.network.ProcessJob(r.RunnerConfig, jobCredentials) 100 if err != nil { 101 return err 102 } 103 104 defer trace.Fail(err, common.NoneFailure) 105 106 err = newBuild.Run(config, trace) 107 108 r.postBuild() 109 110 return 111 } 112 113 func (r *RunSingleCommand) checkFinishedConditions() { 114 if r.MaxBuilds < 1 && !r.runForever { 115 logrus.Println("This runner has processed its build limit, so now exiting") 116 r.finished.Set() 117 } 118 if r.WaitTimeout > 0 && int(time.Since(r.lastBuild).Seconds()) > r.WaitTimeout { 119 logrus.Println("This runner has not received a job in", r.WaitTimeout, "seconds, so now exiting") 120 r.finished.Set() 121 } 122 return 123 } 124 125 func (r *RunSingleCommand) Execute(c *cli.Context) { 126 if len(r.URL) == 0 { 127 logrus.Fatalln("Missing URL") 128 } 129 if len(r.Token) == 0 { 130 logrus.Fatalln("Missing Token") 131 } 132 if len(r.Executor) == 0 { 133 logrus.Fatalln("Missing Executor") 134 } 135 136 executorProvider := common.GetExecutor(r.Executor) 137 if executorProvider == nil { 138 logrus.Fatalln("Unknown executor:", r.Executor) 139 } 140 141 logrus.Println("Starting runner for", r.URL, "with token", r.ShortDescription(), "...") 142 143 r.finished = abool.New() 144 abortSignal := make(chan os.Signal) 145 doneSignal := make(chan int, 1) 146 r.runForever = r.MaxBuilds == 0 147 148 go waitForInterrupts(r.finished, abortSignal, doneSignal, r.interruptSignals) 149 150 r.lastBuild = time.Now() 151 152 for !r.finished.IsSet() { 153 data, err := executorProvider.Acquire(&r.RunnerConfig) 154 if err != nil { 155 logrus.Warningln("Executor update:", err) 156 } 157 158 pErr := r.processBuild(data, abortSignal) 159 if pErr != nil { 160 logrus.WithError(pErr).Error("Failed to process build") 161 } 162 163 r.checkFinishedConditions() 164 executorProvider.Release(&r.RunnerConfig, data) 165 } 166 167 doneSignal <- 0 168 } 169 170 func init() { 171 common.RegisterCommand2("run-single", "start single runner", &RunSingleCommand{ 172 network: network.NewGitLabClient(), 173 }) 174 }