package components
import (
"encoding/json"
"fleetmanager/logger"
"fleetmanager/workflow/directer"
"fleetmanager/workflow/meta"
"fmt"
"time"
)
type BaseTask struct {
Logger *logger.FMLogger
Directer directer.Directer
Step int
Prev int
Next int
meta meta.TaskMeta
retryTimes int
rollbackRetryTimes int
}
func (t *BaseTask) TaskStep() int {
return t.Step
}
func (t *BaseTask) LinkPrev(prev int) {
t.Prev = prev
}
func (t *BaseTask) LinkNext(next int) {
t.Next = next
}
func (t *BaseTask) Execute(*directer.ExecuteContext) (interface{}, error) {
t.ExecNext(nil, nil)
return nil, nil
}
func (t *BaseTask) Rollback(*directer.ExecuteContext) (output interface{}, err error) {
t.RollbackPrev(output, err)
return nil, nil
}
func NewBaseTask(meta meta.TaskMeta, directer directer.Directer, step int) BaseTask {
log := directer.GetLogger().WithFields(map[string]interface{}{
logger.TaskStep: step,
logger.TaskType: meta.TaskType,
logger.TaskName: meta.Name,
logger.TaskDescription: meta.Description,
})
t := BaseTask{
Step: step,
Directer: directer,
meta: meta,
Logger: log,
}
return t
}
func (t *BaseTask) needRetry(d directer.WorkflowDirection) bool {
if d == directer.PositiveDirection {
if t.retryTimes < t.meta.ExecuteFailure.RetryPolicy.Repeat {
t.retryTimes++
return true
}
}
if d == directer.NegativeDirection {
if t.rollbackRetryTimes < t.meta.RollbackFailure.RetryPolicy.Repeat {
t.rollbackRetryTimes++
return true
}
}
return false
}
func (t *BaseTask) getRetryDelay(d directer.WorkflowDirection) int {
if d == directer.PositiveDirection {
return t.meta.ExecuteFailure.RetryPolicy.GetRetryDelay(t.retryTimes)
}
if d == directer.NegativeDirection {
return t.meta.RollbackFailure.RetryPolicy.GetRetryDelay(t.rollbackRetryTimes)
}
return 0
}
func (t *BaseTask) stepLog(ctx *directer.ExecuteContext) {
success := 1
if ctx.Err != nil {
success = 0
}
t.Logger.WithFields(map[string]interface{}{
logger.WorkflowDirection: ctx.Direction,
logger.TaskRetryTimes: t.retryTimes,
logger.TaskRetryDelay: t.getRetryDelay(ctx.Direction),
logger.Success: success,
logger.Error: fmt.Sprintf("%v", ctx.Err),
}).Info("task step log")
}
func (t *BaseTask) ExecNext(output interface{}, err error) {
retryDelay := 0
ctx := &directer.ExecuteContext{
FromOutput: output,
Err: err,
FromType: t.meta.TaskType,
From: t.Step,
RetryTimes: t.retryTimes,
Direction: directer.PositiveDirection,
}
if err != nil {
if t.needRetry(ctx.Direction) {
ctx.Next = t.Step
retryDelay = t.getRetryDelay(ctx.Direction)
} else {
if t.meta.ExecuteFailure.Ignore {
ctx.Next = t.Next
} else {
ctx.Next = t.Step
ctx.Direction = directer.NegativeDirection
}
}
} else {
ctx.Next = t.Next
}
t.stepLog(ctx)
if retryDelay > 0 {
time.Sleep(time.Duration(retryDelay) * time.Second)
}
t.Directer.Process(ctx)
}
func (t *BaseTask) RollbackPrev(output interface{}, err error) {
retryDelay := 0
ctx := &directer.ExecuteContext{
FromOutput: output,
Err: err,
FromType: t.meta.TaskType,
From: t.Step,
RetryTimes: t.retryTimes,
Direction: directer.NegativeDirection,
}
if err != nil {
if t.needRetry(ctx.Direction) {
ctx.Next = t.Step
retryDelay = t.getRetryDelay(ctx.Direction)
} else {
if t.meta.RollbackFailure.Ignore {
ctx.Next = t.Prev
} else {
ctx.Next = 0
}
}
} else {
ctx.Next = t.Prev
}
t.stepLog(ctx)
if retryDelay > 0 {
time.Sleep(time.Duration(retryDelay) * time.Second)
}
t.Directer.Process(ctx)
}
func (t *BaseTask) ParseInput(input interface{}, parameter interface{}) error {
b, ok := input.([]byte)
if ok {
return json.Unmarshal(b, parameter)
}
b, err := json.Marshal(input)
if err != nil {
return err
}
return json.Unmarshal(b, parameter)
}