|
|
|
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
|
|
|
// Use of this source code is governed by the Polyform License
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package pipeline
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"git.awesome-for.me/liuzhiguo/drone-go/drone"
|
|
|
|
)
|
|
|
|
|
|
|
|
// State stores the pipeline state.
|
|
|
|
type State struct {
|
|
|
|
sync.Mutex
|
|
|
|
|
|
|
|
Build *drone.Build
|
|
|
|
Repo *drone.Repo
|
|
|
|
Stage *drone.Stage
|
|
|
|
System *drone.System
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cancel cancels the pipeline.
|
|
|
|
func (s *State) Cancel() {
|
|
|
|
s.Lock()
|
|
|
|
s.skipall()
|
|
|
|
s.killall()
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cancelled returns true if the pipeline is cancelled.
|
|
|
|
func (s *State) Cancelled() bool {
|
|
|
|
s.Lock()
|
|
|
|
v := s.killed()
|
|
|
|
s.Unlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fail fails the named pipeline step with error.
|
|
|
|
func (s *State) Fail(name string, err error) {
|
|
|
|
s.Lock()
|
|
|
|
v := s.find(name)
|
|
|
|
s.fail(v, err)
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// FailAll fails the entire pipeline.
|
|
|
|
func (s *State) FailAll(err error) {
|
|
|
|
s.Lock()
|
|
|
|
s.failAll(err)
|
|
|
|
s.skipall()
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Failed returns true if the pipeline failed.
|
|
|
|
func (s *State) Failed() bool {
|
|
|
|
s.Lock()
|
|
|
|
v := s.failed()
|
|
|
|
s.Unlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip skips the named pipeline step.
|
|
|
|
func (s *State) Skip(name string) {
|
|
|
|
s.Lock()
|
|
|
|
v := s.find(name)
|
|
|
|
s.skip(v)
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// SkipAll skips all pipeline steps.
|
|
|
|
func (s *State) SkipAll() {
|
|
|
|
s.Lock()
|
|
|
|
s.skipall()
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Start sets the named pipeline step to started.
|
|
|
|
func (s *State) Start(name string) {
|
|
|
|
s.Lock()
|
|
|
|
v := s.find(name)
|
|
|
|
s.start(v)
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finish sets the pipeline step to finished.
|
|
|
|
func (s *State) Finish(name string, code int) {
|
|
|
|
s.Lock()
|
|
|
|
v := s.find(name)
|
|
|
|
s.finish(v, code)
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// FinishAll finishes all pipeline steps.
|
|
|
|
func (s *State) FinishAll() {
|
|
|
|
s.Lock()
|
|
|
|
s.finishAll()
|
|
|
|
s.update()
|
|
|
|
s.Unlock()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finished returns true if the step is finished.
|
|
|
|
func (s *State) Finished(name string) bool {
|
|
|
|
s.Lock()
|
|
|
|
v := s.find(name)
|
|
|
|
d := s.finished(v)
|
|
|
|
s.Unlock()
|
|
|
|
return d
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find returns the named pipeline step.
|
|
|
|
func (s *State) Find(name string) *drone.Step {
|
|
|
|
s.Lock()
|
|
|
|
v := s.find(name)
|
|
|
|
s.Unlock()
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Helper functions. INTERNAL USE ONLY
|
|
|
|
//
|
|
|
|
|
|
|
|
// helper function skips all pipeline steps.
|
|
|
|
func (s *State) skipall() {
|
|
|
|
for _, v := range s.Stage.Steps {
|
|
|
|
s.skip(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function that updates the state of an individual step
|
|
|
|
// to indicate the step to skipped.
|
|
|
|
func (s *State) skip(v *drone.Step) {
|
|
|
|
if v.Status == drone.StatusPending {
|
|
|
|
v.Started = time.Now().Unix()
|
|
|
|
v.Stopped = time.Now().Unix()
|
|
|
|
v.Status = drone.StatusSkipped
|
|
|
|
v.ExitCode = 0
|
|
|
|
v.Error = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function kills all pipeline steps.
|
|
|
|
func (s *State) killall() {
|
|
|
|
s.Stage.Error = ""
|
|
|
|
s.Stage.ExitCode = 0
|
|
|
|
s.Stage.Status = drone.StatusKilled
|
|
|
|
s.Stage.Stopped = time.Now().Unix()
|
|
|
|
if s.Stage.Started == 0 {
|
|
|
|
s.Stage.Started = s.Stage.Stopped
|
|
|
|
}
|
|
|
|
for _, v := range s.Stage.Steps {
|
|
|
|
s.kill(v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function that updates the state of an individual step
|
|
|
|
// to indicate the step to killed.
|
|
|
|
func (s *State) kill(v *drone.Step) {
|
|
|
|
if v.Status == drone.StatusRunning {
|
|
|
|
v.Status = drone.StatusKilled
|
|
|
|
v.Stopped = time.Now().Unix()
|
|
|
|
v.ExitCode = 137
|
|
|
|
v.Error = ""
|
|
|
|
if v.Started == 0 {
|
|
|
|
v.Started = v.Stopped
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function returns true if the overall pipeline status
|
|
|
|
// is killed.
|
|
|
|
func (s *State) killed() bool {
|
|
|
|
return s.Stage.Status == drone.StatusKilled
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function that updates the state of an individual step
|
|
|
|
// to indicate the step is started.
|
|
|
|
func (s *State) start(v *drone.Step) {
|
|
|
|
if v.Status == drone.StatusPending {
|
|
|
|
v.Status = drone.StatusRunning
|
|
|
|
v.Started = time.Now().Unix()
|
|
|
|
v.Stopped = 0
|
|
|
|
v.ExitCode = 0
|
|
|
|
v.Error = ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function updates the state of an individual step
|
|
|
|
// based on the exit code.
|
|
|
|
func (s *State) finish(v *drone.Step, code int) {
|
|
|
|
switch v.Status {
|
|
|
|
case drone.StatusRunning, drone.StatusPending:
|
|
|
|
default:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v.ExitCode = code
|
|
|
|
v.Stopped = time.Now().Unix()
|
|
|
|
if v.Started == 0 {
|
|
|
|
v.Started = v.Stopped
|
|
|
|
}
|
|
|
|
switch code {
|
|
|
|
case 0, 78:
|
|
|
|
v.Status = drone.StatusPassing
|
|
|
|
default:
|
|
|
|
v.Status = drone.StatusFailing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function returns true if the step is finished.
|
|
|
|
func (s *State) finished(v *drone.Step) bool {
|
|
|
|
switch v.Status {
|
|
|
|
case drone.StatusRunning, drone.StatusPending:
|
|
|
|
return false
|
|
|
|
default:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function finishes all pipeline steps.
|
|
|
|
func (s *State) finishAll() {
|
|
|
|
for _, v := range s.Stage.Steps {
|
|
|
|
switch v.Status {
|
|
|
|
case drone.StatusPending:
|
|
|
|
s.skip(v)
|
|
|
|
case drone.StatusRunning:
|
|
|
|
s.finish(v, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch s.Stage.Status {
|
|
|
|
case drone.StatusRunning, drone.StatusPending:
|
|
|
|
s.Stage.Stopped = time.Now().Unix()
|
|
|
|
s.Stage.Status = drone.StatusPassing
|
|
|
|
if s.Stage.Started == 0 {
|
|
|
|
s.Stage.Started = s.Stage.Stopped
|
|
|
|
}
|
|
|
|
if s.failed() {
|
|
|
|
s.Stage.Status = drone.StatusFailing
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
if s.Stage.Started == 0 {
|
|
|
|
s.Stage.Started = time.Now().Unix()
|
|
|
|
}
|
|
|
|
if s.Stage.Stopped == 0 {
|
|
|
|
s.Stage.Stopped = time.Now().Unix()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function fails an individual step.
|
|
|
|
func (s *State) fail(v *drone.Step, err error) {
|
|
|
|
v.Status = drone.StatusError
|
|
|
|
v.Error = err.Error()
|
|
|
|
v.ExitCode = 255
|
|
|
|
v.Stopped = time.Now().Unix()
|
|
|
|
if v.Started == 0 {
|
|
|
|
v.Started = v.Stopped
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function fails the overall pipeline.
|
|
|
|
func (s *State) failAll(err error) {
|
|
|
|
switch s.Stage.Status {
|
|
|
|
case drone.StatusPending,
|
|
|
|
drone.StatusRunning:
|
|
|
|
s.Stage.Status = drone.StatusError
|
|
|
|
s.Stage.Error = err.Error()
|
|
|
|
s.Stage.Stopped = time.Now().Unix()
|
|
|
|
s.Stage.ExitCode = 255
|
|
|
|
if s.Stage.Started == 0 {
|
|
|
|
s.Stage.Started = s.Stage.Stopped
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function returns true if the overall pipeline status
|
|
|
|
// is failing.
|
|
|
|
func (s *State) failed() bool {
|
|
|
|
switch s.Stage.Status {
|
|
|
|
case drone.StatusFailing,
|
|
|
|
drone.StatusError,
|
|
|
|
drone.StatusKilled:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for _, v := range s.Stage.Steps {
|
|
|
|
if v.ErrIgnore {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch v.Status {
|
|
|
|
case drone.StatusFailing,
|
|
|
|
drone.StatusError,
|
|
|
|
drone.StatusKilled:
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function updates the build and stage based on the
|
|
|
|
// aggregate
|
|
|
|
func (s *State) update() {
|
|
|
|
for _, v := range s.Stage.Steps {
|
|
|
|
switch v.Status {
|
|
|
|
case drone.StatusKilled:
|
|
|
|
s.Stage.ExitCode = 137
|
|
|
|
s.Stage.Status = drone.StatusKilled
|
|
|
|
s.Build.Status = drone.StatusKilled
|
|
|
|
return
|
|
|
|
case drone.StatusError:
|
|
|
|
s.Stage.Error = v.Error
|
|
|
|
s.Stage.ExitCode = 255
|
|
|
|
s.Stage.Status = drone.StatusError
|
|
|
|
s.Build.Status = drone.StatusError
|
|
|
|
return
|
|
|
|
case drone.StatusFailing:
|
|
|
|
if v.ErrIgnore == false {
|
|
|
|
s.Stage.Status = drone.StatusFailing
|
|
|
|
s.Build.Status = drone.StatusFailing
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// helper function finds the step by name.
|
|
|
|
func (s *State) find(name string) *drone.Step {
|
|
|
|
for _, step := range s.Stage.Steps {
|
|
|
|
if step.Name == name {
|
|
|
|
return step
|
|
|
|
}
|
|
|
|
}
|
|
|
|
panic("step not found: " + name)
|
|
|
|
}
|