286 lines
8.1 KiB
Go
286 lines
8.1 KiB
Go
|
/*
|
||
|
* Tencent is pleased to support the open source community by making Blueking Container Service available.
|
||
|
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
||
|
* Licensed under the MIT License (the "License"); you may not use this file except
|
||
|
* in compliance with the License. You may obtain a copy of the License at
|
||
|
* http://opensource.org/licenses/MIT
|
||
|
* Unless required by applicable law or agreed to in writing, software distributed under
|
||
|
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||
|
* either express or implied. See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
// Package task is a package for task management
|
||
|
package task
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"time"
|
||
|
|
||
|
types "git.ifooth.com/common/machinery-plugins/task/types"
|
||
|
)
|
||
|
|
||
|
// State is a struct for task state
|
||
|
type State struct {
|
||
|
task *types.Task
|
||
|
currentStep string
|
||
|
callBack func(isSuccess bool, task *types.Task)
|
||
|
}
|
||
|
|
||
|
// NewState return state relative to task
|
||
|
func NewState(task *types.Task, currentStep string) *State {
|
||
|
return &State{
|
||
|
task: task,
|
||
|
currentStep: currentStep,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// isTaskTerminated is terminated
|
||
|
func (s *State) isTaskTerminated() bool {
|
||
|
status := s.task.GetStatus()
|
||
|
if status == types.TaskStatusFailure || status == types.TaskStatusForceTerminate ||
|
||
|
status == types.TaskStatusTimeout || status == types.TaskStatusSuccess {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// isReadyToStep check if step is ready to step
|
||
|
func (s *State) isReadyToStep(stepName string) (*types.Step, error) {
|
||
|
switch s.task.GetStatus() {
|
||
|
case types.TaskStatusRunning, types.TaskStatusInit:
|
||
|
case types.TaskStatusForceTerminate:
|
||
|
return nil, fmt.Errorf("task %s state for terminate", s.task.GetTaskID())
|
||
|
default:
|
||
|
return nil, fmt.Errorf("task %s is not running, state is %s", s.task.GetTaskID(), s.task.GetStatus())
|
||
|
}
|
||
|
|
||
|
// validate step existence
|
||
|
curStep, ok := s.task.GetStep(stepName)
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("step %s is not exist", stepName)
|
||
|
}
|
||
|
|
||
|
// return nil & nil means step had been executed
|
||
|
if curStep.GetStatus() == types.TaskStatusSuccess {
|
||
|
// step is success, skip
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
// not first time to execute current step
|
||
|
if stepName == s.task.GetCurrentStep() {
|
||
|
if curStep.GetStatus() == types.TaskStatusFailure {
|
||
|
curStep.AddRetryCount(1)
|
||
|
}
|
||
|
|
||
|
nowTime := time.Now()
|
||
|
curStep = curStep.SetStartTime(nowTime).
|
||
|
SetStatus(types.TaskStatusRunning).
|
||
|
SetMessage("step ready to run").
|
||
|
SetLastUpdate(nowTime)
|
||
|
|
||
|
// update Task in storage
|
||
|
if err := getGlobalStorage().UpdateTask(context.Background(), s.task); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return curStep, nil
|
||
|
}
|
||
|
|
||
|
// first time to execute step
|
||
|
for _, name := range s.task.StepSequence {
|
||
|
step, ok := s.task.GetStep(name)
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("step %s is not exist", stepName)
|
||
|
}
|
||
|
|
||
|
// find current step
|
||
|
if name == stepName {
|
||
|
// step already success
|
||
|
if step.GetStatus() == types.TaskStatusSuccess {
|
||
|
return nil, fmt.Errorf("task %s step %s already success", s.task.GetTaskID(), stepName)
|
||
|
}
|
||
|
// set current step
|
||
|
nowTime := time.Now()
|
||
|
s.task.SetCurrentStep(stepName)
|
||
|
step = step.SetStartTime(nowTime).
|
||
|
SetStatus(types.TaskStatusRunning).
|
||
|
SetMessage("step ready to run").
|
||
|
SetLastUpdate(nowTime)
|
||
|
|
||
|
// update Task in storage
|
||
|
if err := getGlobalStorage().UpdateTask(context.Background(), s.task); err != nil {
|
||
|
return nil, fmt.Errorf("update task %s step %s status error", s.task.GetTaskID(), stepName)
|
||
|
}
|
||
|
return step, nil
|
||
|
}
|
||
|
|
||
|
// skip step if step allow skipOnFailed
|
||
|
if step.SkipOnFailed {
|
||
|
continue
|
||
|
}
|
||
|
// previous step execute failure
|
||
|
if step.GetStatus() != types.TaskStatusSuccess {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
// previous step execute failure
|
||
|
return nil, fmt.Errorf("step %s is not ready", stepName)
|
||
|
}
|
||
|
|
||
|
// updateStepSuccess update step status to success
|
||
|
func (s *State) updateStepSuccess(start time.Time, stepName string) error {
|
||
|
step, ok := s.task.GetStep(stepName)
|
||
|
if !ok {
|
||
|
return fmt.Errorf("step %s is not exist", stepName)
|
||
|
}
|
||
|
endTime := time.Now()
|
||
|
|
||
|
step.SetStartTime(start).
|
||
|
SetEndTime(endTime).
|
||
|
SetExecutionTime(start, endTime).
|
||
|
SetStatus(types.TaskStatusSuccess).
|
||
|
SetMessage(fmt.Sprintf("step %s running successfully", step.Name)).
|
||
|
SetLastUpdate(endTime)
|
||
|
|
||
|
s.task.SetStatus(types.TaskStatusRunning).
|
||
|
SetMessage(fmt.Sprintf("step %s running successfully", step.Name)).
|
||
|
SetLastUpdate(endTime)
|
||
|
|
||
|
// last step
|
||
|
if s.isLastStep(stepName) {
|
||
|
taskStartTime, err := s.task.GetStartTime()
|
||
|
if err != nil {
|
||
|
return fmt.Errorf(fmt.Sprintf("get task %s start time error", s.task.GetTaskID()))
|
||
|
}
|
||
|
s.task.SetEndTime(endTime).
|
||
|
SetExecutionTime(taskStartTime, endTime).
|
||
|
SetStatus(types.TaskStatusSuccess).
|
||
|
SetMessage("task finished successfully")
|
||
|
|
||
|
// callback
|
||
|
if s.callBack != nil {
|
||
|
s.callBack(true, s.task)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// update Task in storage
|
||
|
if err := getGlobalStorage().UpdateTask(context.Background(), s.task); err != nil {
|
||
|
return fmt.Errorf("update task %s status error", s.task.GetTaskID())
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// updateStepFailure update step status to failure
|
||
|
func (s *State) updateStepFailure(start time.Time, stepName string, stepErr error) error {
|
||
|
step, ok := s.task.GetStep(stepName)
|
||
|
if !ok {
|
||
|
return fmt.Errorf("step %s is not exist", stepName)
|
||
|
}
|
||
|
endTime := time.Now()
|
||
|
|
||
|
step.SetStartTime(start).
|
||
|
SetEndTime(endTime).
|
||
|
SetExecutionTime(start, endTime).
|
||
|
SetStatus(types.TaskStatusFailure).
|
||
|
SetMessage(fmt.Sprintf("running failed, %s", stepErr.Error())).
|
||
|
SetLastUpdate(endTime)
|
||
|
|
||
|
// if step SkipOnFailed, update task status to running
|
||
|
if step.GetSkipOnFailed() {
|
||
|
// skip, set task running or success
|
||
|
s.task.SetStatus(types.TaskStatusRunning).
|
||
|
SetMessage(fmt.Sprintf("step %s running failed", step.Name)).
|
||
|
SetLastUpdate(endTime)
|
||
|
|
||
|
// last step failed and skipOnFailed is true, update task status to success
|
||
|
if s.isLastStep(stepName) {
|
||
|
// last step
|
||
|
taskStartTime, err := s.task.GetStartTime()
|
||
|
if err != nil {
|
||
|
return fmt.Errorf(fmt.Sprintf("get task %s start time error", s.task.GetTaskID()))
|
||
|
}
|
||
|
s.task.SetStatus(types.TaskStatusSuccess).
|
||
|
SetMessage("task finished successfully").
|
||
|
SetLastUpdate(endTime).
|
||
|
SetEndTime(endTime).
|
||
|
SetExecutionTime(taskStartTime, endTime)
|
||
|
|
||
|
// callback
|
||
|
if s.callBack != nil {
|
||
|
s.callBack(true, s.task)
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// not skip, set task faile
|
||
|
s.task.SetStatus(types.TaskStatusFailure).
|
||
|
SetMessage(fmt.Sprintf("step %s running failed", step.Name)).
|
||
|
SetLastUpdate(endTime).
|
||
|
SetEndTime(endTime).
|
||
|
SetExecutionTime(start, endTime)
|
||
|
|
||
|
// callback
|
||
|
if s.callBack != nil {
|
||
|
s.callBack(false, s.task)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// update Task in storage
|
||
|
if err := getGlobalStorage().UpdateTask(context.Background(), s.task); err != nil {
|
||
|
return fmt.Errorf("update task %s status error", s.task.GetTaskID())
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *State) isLastStep(stepName string) bool {
|
||
|
return stepName == s.task.StepSequence[len(s.task.StepSequence)-1]
|
||
|
}
|
||
|
|
||
|
// GetCommonParams get common params by key
|
||
|
func (s *State) GetCommonParams(key string) (string, bool) {
|
||
|
return s.task.GetCommonParams(key)
|
||
|
}
|
||
|
|
||
|
// AddCommonParams add common params
|
||
|
func (s *State) AddCommonParams(key, value string) *State {
|
||
|
s.task.AddCommonParams(key, value)
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// GetExtraParams get extra params by obj
|
||
|
func (s *State) GetExtraParams(obj interface{}) error {
|
||
|
return s.task.GetExtra(obj)
|
||
|
}
|
||
|
|
||
|
// SetExtraAll set extra params by obj
|
||
|
func (s *State) SetExtraAll(obj interface{}) error {
|
||
|
return s.task.SetExtraAll(obj)
|
||
|
}
|
||
|
|
||
|
// GetStepParam get step params by key
|
||
|
func (s *State) GetStepParam(stepName, key string) (string, bool) {
|
||
|
return s.task.GetStepParam(stepName, key)
|
||
|
}
|
||
|
|
||
|
// AddStepParams add step params
|
||
|
func (s *State) AddStepParams(stepName, key, value string) error {
|
||
|
return s.task.AddStepParams(stepName, key, value)
|
||
|
}
|
||
|
|
||
|
// GetTask get task
|
||
|
func (s *State) GetTask() *types.Task {
|
||
|
return s.task
|
||
|
}
|
||
|
|
||
|
// GetCurrentStep get current step
|
||
|
func (s *State) GetCurrentStep() (*types.Step, bool) {
|
||
|
return s.task.GetStep(s.currentStep)
|
||
|
}
|
||
|
|
||
|
// GetStep get step by stepName
|
||
|
func (s *State) GetStep(stepName string) (*types.Step, bool) {
|
||
|
return s.task.GetStep(stepName)
|
||
|
}
|