github.com/drone/runner-go@v1.12.0/pipeline/state.go (about) 1 // Copyright 2019 Drone.IO Inc. All rights reserved. 2 // Use of this source code is governed by the Polyform License 3 // that can be found in the LICENSE file. 4 5 package pipeline 6 7 import ( 8 "sync" 9 "time" 10 11 "github.com/drone/drone-go/drone" 12 ) 13 14 // State stores the pipeline state. 15 type State struct { 16 sync.Mutex 17 18 Build *drone.Build 19 Repo *drone.Repo 20 Stage *drone.Stage 21 System *drone.System 22 } 23 24 // Cancel cancels the pipeline. 25 func (s *State) Cancel() { 26 s.Lock() 27 s.skipall() 28 s.killall() 29 s.update() 30 s.Unlock() 31 } 32 33 // Cancelled returns true if the pipeline is cancelled. 34 func (s *State) Cancelled() bool { 35 s.Lock() 36 v := s.killed() 37 s.Unlock() 38 return v 39 } 40 41 // Fail fails the named pipeline step with error. 42 func (s *State) Fail(name string, err error) { 43 s.Lock() 44 v := s.find(name) 45 s.fail(v, err) 46 s.update() 47 s.Unlock() 48 } 49 50 // FailAll fails the entire pipeline. 51 func (s *State) FailAll(err error) { 52 s.Lock() 53 s.failAll(err) 54 s.skipall() 55 s.update() 56 s.Unlock() 57 } 58 59 // Failed returns true if the pipeline failed. 60 func (s *State) Failed() bool { 61 s.Lock() 62 v := s.failed() 63 s.Unlock() 64 return v 65 } 66 67 // Skip skips the named pipeline step. 68 func (s *State) Skip(name string) { 69 s.Lock() 70 v := s.find(name) 71 s.skip(v) 72 s.update() 73 s.Unlock() 74 } 75 76 // SkipAll skips all pipeline steps. 77 func (s *State) SkipAll() { 78 s.Lock() 79 s.skipall() 80 s.update() 81 s.Unlock() 82 } 83 84 // Start sets the named pipeline step to started. 85 func (s *State) Start(name string) { 86 s.Lock() 87 v := s.find(name) 88 s.start(v) 89 s.Unlock() 90 } 91 92 // Finish sets the pipeline step to finished. 93 func (s *State) Finish(name string, code int) { 94 s.Lock() 95 v := s.find(name) 96 s.finish(v, code) 97 s.update() 98 s.Unlock() 99 } 100 101 // FinishAll finishes all pipeline steps. 102 func (s *State) FinishAll() { 103 s.Lock() 104 s.finishAll() 105 s.update() 106 s.Unlock() 107 } 108 109 // Finished returns true if the step is finished. 110 func (s *State) Finished(name string) bool { 111 s.Lock() 112 v := s.find(name) 113 d := s.finished(v) 114 s.Unlock() 115 return d 116 } 117 118 // Find returns the named pipeline step. 119 func (s *State) Find(name string) *drone.Step { 120 s.Lock() 121 v := s.find(name) 122 s.Unlock() 123 return v 124 } 125 126 // 127 // Helper functions. INTERNAL USE ONLY 128 // 129 130 // helper function skips all pipeline steps. 131 func (s *State) skipall() { 132 for _, v := range s.Stage.Steps { 133 s.skip(v) 134 } 135 } 136 137 // helper function that updates the state of an individual step 138 // to indicate the step to skipped. 139 func (s *State) skip(v *drone.Step) { 140 if v.Status == drone.StatusPending { 141 v.Started = time.Now().Unix() 142 v.Stopped = time.Now().Unix() 143 v.Status = drone.StatusSkipped 144 v.ExitCode = 0 145 v.Error = "" 146 } 147 } 148 149 // helper function kills all pipeline steps. 150 func (s *State) killall() { 151 s.Stage.Error = "" 152 s.Stage.ExitCode = 0 153 s.Stage.Status = drone.StatusKilled 154 s.Stage.Stopped = time.Now().Unix() 155 if s.Stage.Started == 0 { 156 s.Stage.Started = s.Stage.Stopped 157 } 158 for _, v := range s.Stage.Steps { 159 s.kill(v) 160 } 161 } 162 163 // helper function that updates the state of an individual step 164 // to indicate the step to killed. 165 func (s *State) kill(v *drone.Step) { 166 if v.Status == drone.StatusRunning { 167 v.Status = drone.StatusKilled 168 v.Stopped = time.Now().Unix() 169 v.ExitCode = 137 170 v.Error = "" 171 if v.Started == 0 { 172 v.Started = v.Stopped 173 } 174 } 175 } 176 177 // helper function returns true if the overall pipeline status 178 // is killed. 179 func (s *State) killed() bool { 180 return s.Stage.Status == drone.StatusKilled 181 } 182 183 // helper function that updates the state of an individual step 184 // to indicate the step is started. 185 func (s *State) start(v *drone.Step) { 186 if v.Status == drone.StatusPending { 187 v.Status = drone.StatusRunning 188 v.Started = time.Now().Unix() 189 v.Stopped = 0 190 v.ExitCode = 0 191 v.Error = "" 192 } 193 } 194 195 // helper function updates the state of an individual step 196 // based on the exit code. 197 func (s *State) finish(v *drone.Step, code int) { 198 switch v.Status { 199 case drone.StatusRunning, drone.StatusPending: 200 default: 201 return 202 } 203 v.ExitCode = code 204 v.Stopped = time.Now().Unix() 205 if v.Started == 0 { 206 v.Started = v.Stopped 207 } 208 switch code { 209 case 0, 78: 210 v.Status = drone.StatusPassing 211 default: 212 v.Status = drone.StatusFailing 213 } 214 } 215 216 // helper function returns true if the step is finished. 217 func (s *State) finished(v *drone.Step) bool { 218 switch v.Status { 219 case drone.StatusRunning, drone.StatusPending: 220 return false 221 default: 222 return true 223 } 224 } 225 226 // helper function finishes all pipeline steps. 227 func (s *State) finishAll() { 228 for _, v := range s.Stage.Steps { 229 switch v.Status { 230 case drone.StatusPending: 231 s.skip(v) 232 case drone.StatusRunning: 233 s.finish(v, 0) 234 } 235 } 236 switch s.Stage.Status { 237 case drone.StatusRunning, drone.StatusPending: 238 s.Stage.Stopped = time.Now().Unix() 239 s.Stage.Status = drone.StatusPassing 240 if s.Stage.Started == 0 { 241 s.Stage.Started = s.Stage.Stopped 242 } 243 if s.failed() { 244 s.Stage.Status = drone.StatusFailing 245 } 246 default: 247 if s.Stage.Started == 0 { 248 s.Stage.Started = time.Now().Unix() 249 } 250 if s.Stage.Stopped == 0 { 251 s.Stage.Stopped = time.Now().Unix() 252 } 253 } 254 } 255 256 // helper function fails an individual step. 257 func (s *State) fail(v *drone.Step, err error) { 258 v.Status = drone.StatusError 259 v.Error = err.Error() 260 v.ExitCode = 255 261 v.Stopped = time.Now().Unix() 262 if v.Started == 0 { 263 v.Started = v.Stopped 264 } 265 } 266 267 // helper function fails the overall pipeline. 268 func (s *State) failAll(err error) { 269 switch s.Stage.Status { 270 case drone.StatusPending, 271 drone.StatusRunning: 272 s.Stage.Status = drone.StatusError 273 s.Stage.Error = err.Error() 274 s.Stage.Stopped = time.Now().Unix() 275 s.Stage.ExitCode = 255 276 if s.Stage.Started == 0 { 277 s.Stage.Started = s.Stage.Stopped 278 } 279 } 280 } 281 282 // helper function returns true if the overall pipeline status 283 // is failing. 284 func (s *State) failed() bool { 285 switch s.Stage.Status { 286 case drone.StatusFailing, 287 drone.StatusError, 288 drone.StatusKilled: 289 return true 290 } 291 for _, v := range s.Stage.Steps { 292 if v.ErrIgnore { 293 continue 294 } 295 switch v.Status { 296 case drone.StatusFailing, 297 drone.StatusError, 298 drone.StatusKilled: 299 return true 300 } 301 } 302 return false 303 } 304 305 // helper function updates the build and stage based on the 306 // aggregate 307 func (s *State) update() { 308 for _, v := range s.Stage.Steps { 309 switch v.Status { 310 case drone.StatusKilled: 311 s.Stage.ExitCode = 137 312 s.Stage.Status = drone.StatusKilled 313 s.Build.Status = drone.StatusKilled 314 return 315 case drone.StatusError: 316 s.Stage.Error = v.Error 317 s.Stage.ExitCode = 255 318 s.Stage.Status = drone.StatusError 319 s.Build.Status = drone.StatusError 320 return 321 case drone.StatusFailing: 322 if v.ErrIgnore == false { 323 s.Stage.Status = drone.StatusFailing 324 s.Build.Status = drone.StatusFailing 325 return 326 } 327 } 328 } 329 } 330 331 // helper function finds the step by name. 332 func (s *State) find(name string) *drone.Step { 333 for _, step := range s.Stage.Steps { 334 if step.Name == name { 335 return step 336 } 337 } 338 panic("step not found: " + name) 339 }