job.go 6.7 KB
Newer Older
aaronchen2k2k's avatar
aaronchen2k2k 已提交
1 2 3
package service

import (
Z
zhaoke 已提交
4 5 6 7 8 9 10 11
	"errors"
	"fmt"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

aaronchen2k2k's avatar
aaronchen2k2k 已提交
12
	commConsts "github.com/easysoft/zentaoatf/internal/pkg/consts"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
13
	commDomain "github.com/easysoft/zentaoatf/internal/pkg/domain"
14 15 16 17
	analysisHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/analysis"
	execHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/exec"
	scriptHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/script"
	zentaoHelper "github.com/easysoft/zentaoatf/internal/pkg/helper/zentao"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
18
	serverConfig "github.com/easysoft/zentaoatf/internal/server/config"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
19 20 21
	serverDomain "github.com/easysoft/zentaoatf/internal/server/modules/v1/domain"
	"github.com/easysoft/zentaoatf/internal/server/modules/v1/model"
	"github.com/easysoft/zentaoatf/internal/server/modules/v1/repo"
22 23
	channelUtils "github.com/easysoft/zentaoatf/pkg/lib/channel"
	fileUtils "github.com/easysoft/zentaoatf/pkg/lib/file"
Z
zhaoke 已提交
24
	shellUtils "github.com/easysoft/zentaoatf/pkg/lib/shell"
aaronchen2k2k's avatar
aaronchen2k2k 已提交
25 26
)

27 28 29 30
var (
	channelMap sync.Map
)

aaronchen2k2k's avatar
aaronchen2k2k 已提交
31
type JobService struct {
32
	JobRepo *repo.JobRepo `inject:""`
aaronchen2k2k's avatar
aaronchen2k2k 已提交
33 34 35 36 37 38
}

func NewJobService() *JobService {
	return &JobService{}
}

39 40 41 42 43
func (s *JobService) Add(req serverDomain.ZentaoExecReq) (err error) {
	po := model.Job{
		Workspace: req.Workspace,
		Path:      req.Path,
		Ids:       req.Ids,
44
		Cmd:       req.Cmd,
45 46 47 48 49 50 51

		Task:   req.Task,
		Retry:  1,
		Status: commConsts.JobCreated,
	}

	s.JobRepo.Save(&po)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
52 53 54 55

	return
}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
56
func (s *JobService) Start(po *model.Job) {
57 58
	ch := make(chan int, 1)
	channelMap.Store(po.ID, ch)
雨爱无痕 已提交
59
	commConsts.ExecFrom = commConsts.FromZentao
aaronchen2k2k's avatar
aaronchen2k2k 已提交
60

61
	req := s.genExecReqFromJob(*po)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
62

63
	go func() {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
64
		s.JobRepo.UpdateStatus(po, commConsts.JobInprogress, true, false)
65

Z
zhaoke 已提交
66 67 68 69
		if po.Cmd != "" {
			shellUtils.ExeShellWithOutput(po.Cmd)
		}

Z
zhaoke 已提交
70
		err := execHelper.Exec(nil, req, nil)
71

aaronchen2k2k's avatar
aaronchen2k2k 已提交
72
		s.JobRepo.UpdateStatus(po, commConsts.JobCompleted, false, true)
73

Z
zhaoke 已提交
74
		// s.SubmitJobStatus(*po)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
75

Z
zhaoke 已提交
76
		s.SubmitExecResult(*po, err)
77 78 79 80 81 82

		if ch != nil {
			channelMap.Delete(po.ID)
			close(ch)
		}
	}()
aaronchen2k2k's avatar
aaronchen2k2k 已提交
83 84
}

85 86
func (s *JobService) Cancel(id uint) {
	taskInfo, _ := s.JobRepo.Get(id)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
87

88 89
	if taskInfo.ID > 0 {
		s.JobRepo.SetCanceled(taskInfo)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
90 91
	}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
92 93 94 95 96 97 98 99 100 101
	s.stop(id)
}

func (s *JobService) Restart(po *model.Job) (ret bool) {
	s.stop(po.ID)
	s.Start(po)

	s.JobRepo.AddRetry(po)

	return
aaronchen2k2k's avatar
aaronchen2k2k 已提交
102 103
}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
104
func (s *JobService) stop(id uint) {
105
	chVal, ok := channelMap.Load(id)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
106

107 108 109 110 111 112 113 114 115 116 117 118 119 120
	if !ok || chVal == nil {
		return
	}

	channelMap.Delete(id)

	ch := chVal.(chan int)
	if ch != nil {
		if !channelUtils.IsChanClose(ch) {
			ch <- 1
		}

		ch = nil
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
121 122
}

123
func (s *JobService) Check() (err error) {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
124
	taskMap, _ := s.Query()
125 126 127 128 129

	toStartNewJob := false
	if len(taskMap.Inprogress) > 0 {
		runningJob := taskMap.Inprogress[0]

aaronchen2k2k's avatar
aaronchen2k2k 已提交
130 131
		if s.IsError(*runningJob) || s.IsTimeout(*runningJob) || s.isEmpty() {
			if s.NeedRetry(*runningJob) {
132 133
				s.Restart(runningJob)
			} else {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
134 135 136
				s.JobRepo.UpdateStatus(runningJob, commConsts.JobFailed, false, true)
				s.SubmitJobStatus(*runningJob)

137 138 139 140 141 142
				toStartNewJob = true
			}
		}

	} else {
		toStartNewJob = true
aaronchen2k2k's avatar
aaronchen2k2k 已提交
143 144
	}

145 146 147 148 149
	if toStartNewJob && len(taskMap.Created) > 0 {
		newJob := taskMap.Created[0]

		s.Start(newJob)
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
150 151 152 153

	return
}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
func (s *JobService) List(status string) (jobs []model.Job, err error) {
	status = strings.TrimSpace(status)
	jobs, err = s.JobRepo.ListByStatus(status)

	return
}

func (s *JobService) Query() (ret serverDomain.JobQueryResp, err error) {
	//ret = serverDomain.JobQueryResp{
	//	Created:    make([]model.Job, 0),
	//	Inprogress: make([]model.Job, 0),
	//	Canceled:   make([]model.Job, 0),
	//	Completed:  make([]model.Job, 0),
	//	Failed:     make([]model.Job, 0),
	//}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
169

170
	pos, _ := s.JobRepo.Query()
aaronchen2k2k's avatar
aaronchen2k2k 已提交
171

172 173 174 175 176
	for _, po := range pos {
		status := po.Status
		if status == commConsts.JobTimeout || status == commConsts.JobError {
			status = commConsts.JobInprogress
		}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
177

178
		if status == commConsts.JobCreated {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
179
			ret.Created = append(ret.Created, &po)
180
		} else if status == commConsts.JobInprogress {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
181
			ret.Inprogress = append(ret.Inprogress, &po)
182
		} else if status == commConsts.JobCanceled {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
183
			ret.Canceled = append(ret.Canceled, &po)
184
		} else if status == commConsts.JobCompleted {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
185
			ret.Completed = append(ret.Completed, &po)
186
		} else if status == commConsts.JobFailed {
aaronchen2k2k's avatar
aaronchen2k2k 已提交
187
			ret.Failed = append(ret.Failed, &po)
188 189
		}
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
190 191 192 193

	return
}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
194 195 196 197
func (s *JobService) SubmitJobStatus(job model.Job) (err error) {
	status := serverDomain.ZentaoJobSubmitReq{
		Task:      job.Task,
		Status:    job.Status,
Z
zhaoke 已提交
198 199
		StartTime: (*job.StartDate).Format("2006-01-02 15:04:05"),
		EndTime:   (*job.EndDate).Format("2006-01-02 15:04:05"),
aaronchen2k2k's avatar
aaronchen2k2k 已提交
200
		RetryTime: job.Retry,
Z
zhaoke 已提交
201 202
		Error:     "",
		Data:      "",
aaronchen2k2k's avatar
aaronchen2k2k 已提交
203 204 205 206 207
	}

	config := commDomain.WorkspaceConf{
		Url: serverConfig.CONFIG.Server,
	}
Z
zhaoke 已提交
208
	err = zentaoHelper.JobCommitResult(status, config)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
209 210 211 212

	return
}

Z
zhaoke 已提交
213
func (s *JobService) SubmitExecResult(job model.Job, execErr error) (err error) {
214 215 216 217
	result := serverDomain.ZentaoResultSubmitReq{
		Task: job.Task,
		Seq:  commConsts.ExecLogDir,
	}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
218

Z
zhaoke 已提交
219 220 221 222 223 224 225 226 227
	reportPth := filepath.Join(result.Seq, commConsts.ResultJson)
	var report commDomain.ZtfReport
	if fileUtils.FileExist(reportPth) {
		report, err = analysisHelper.ReadReportByPath(reportPth)
	} else {
		err = errors.New("case not found")
	}
	if err != nil && execErr == nil {
		execErr = err
aaronchen2k2k's avatar
aaronchen2k2k 已提交
228 229
	}

aaronchen2k2k's avatar
aaronchen2k2k 已提交
230 231 232
	config := commDomain.WorkspaceConf{
		Url: serverConfig.CONFIG.Server,
	}
Z
zhaoke 已提交
233 234 235 236 237 238 239 240

	ret := serverDomain.ZentaoJobSubmitReq{
		Task:      job.Task,
		Status:    job.Status,
		StartTime: (*job.StartDate).Format("2006-01-02 15:04:05"),
		EndTime:   (*job.EndDate).Format("2006-01-02 15:04:05"),
		RetryTime: job.Retry,
		Error:     fmt.Sprintf("%v", execErr),
Z
zhaoke 已提交
241
		Data:      report,
Z
zhaoke 已提交
242 243
	}
	err = zentaoHelper.JobCommitResult(ret, config)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
244

245 246 247
	return
}

248
func (s *JobService) genExecReqFromJob(po model.Job) (req serverDomain.ExecReq) {
249 250 251 252 253
	caseIds := make([]int, 0)
	for _, idStr := range strings.Split(po.Ids, ",") {
		id, err := strconv.Atoi(idStr)
		if err == nil {
			caseIds = append(caseIds, id)
aaronchen2k2k's avatar
aaronchen2k2k 已提交
254 255 256
		}
	}

257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
	dir := po.Path
	if !fileUtils.IsAbsolutePath(dir) {
		dir = filepath.Join(po.Workspace, dir)
	}

	caseIdMap := map[int]string{}
	scriptHelper.GetScriptByIdsInDir(dir, &caseIdMap)

	cases := scriptHelper.GetCaseByListInMap(caseIds, caseIdMap)

	req.Act = commConsts.ExecCase
	req.ScriptDirParamFromCmdLine = "."
	req.TestSets = append(req.TestSets, serverDomain.TestSet{
		WorkspacePath: po.Workspace,
		Cases:         cases,
272
		Cmd:           po.Cmd,
273 274
	})

aaronchen2k2k's avatar
aaronchen2k2k 已提交
275 276 277
	return
}

278 279 280
func (s *JobService) IsError(po model.Job) bool {
	return po.Status == commConsts.JobError
}
aaronchen2k2k's avatar
aaronchen2k2k 已提交
281

282 283
func (s *JobService) IsTimeout(po model.Job) bool {
	dur := time.Now().Unix() - po.StartDate.Unix()
aaronchen2k2k's avatar
aaronchen2k2k 已提交
284
	// return dur > 3
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	return po.Status == commConsts.JobInprogress && dur > commConsts.JobTimeoutTime
}

func (s *JobService) NeedRetry(po model.Job) bool {
	return po.Retry < commConsts.JobRetryTime
}

func (s *JobService) isEmpty() bool {
	length := 0

	channelMap.Range(func(key, value interface{}) bool {
		length++
		return true
	})

	return length == 0
aaronchen2k2k's avatar
aaronchen2k2k 已提交
301
}