github.com/jfrerich/mattermost-server@v5.8.0-rc2+incompatible/jobs/jobs.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package jobs 5 6 import ( 7 "context" 8 "fmt" 9 "time" 10 11 "net/http" 12 13 "github.com/mattermost/mattermost-server/mlog" 14 "github.com/mattermost/mattermost-server/model" 15 ) 16 17 const ( 18 CANCEL_WATCHER_POLLING_INTERVAL = 5000 19 ) 20 21 func (srv *JobServer) CreateJob(jobType string, jobData map[string]string) (*model.Job, *model.AppError) { 22 job := model.Job{ 23 Id: model.NewId(), 24 Type: jobType, 25 CreateAt: model.GetMillis(), 26 Status: model.JOB_STATUS_PENDING, 27 Data: jobData, 28 } 29 30 if err := job.IsValid(); err != nil { 31 return nil, err 32 } 33 34 if result := <-srv.Store.Job().Save(&job); result.Err != nil { 35 return nil, result.Err 36 } 37 38 return &job, nil 39 } 40 41 func (srv *JobServer) GetJob(id string) (*model.Job, *model.AppError) { 42 if result := <-srv.Store.Job().Get(id); result.Err != nil { 43 return nil, result.Err 44 } else { 45 return result.Data.(*model.Job), nil 46 } 47 } 48 49 func (srv *JobServer) ClaimJob(job *model.Job) (bool, *model.AppError) { 50 if result := <-srv.Store.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS); result.Err != nil { 51 return false, result.Err 52 } else { 53 success := result.Data.(bool) 54 return success, nil 55 } 56 } 57 58 func (srv *JobServer) SetJobProgress(job *model.Job, progress int64) *model.AppError { 59 job.Status = model.JOB_STATUS_IN_PROGRESS 60 job.Progress = progress 61 62 if result := <-srv.Store.Job().UpdateOptimistically(job, model.JOB_STATUS_IN_PROGRESS); result.Err != nil { 63 return result.Err 64 } else { 65 return nil 66 } 67 } 68 69 func (srv *JobServer) SetJobSuccess(job *model.Job) *model.AppError { 70 result := <-srv.Store.Job().UpdateStatus(job.Id, model.JOB_STATUS_SUCCESS) 71 return result.Err 72 } 73 74 func (srv *JobServer) SetJobError(job *model.Job, jobError *model.AppError) *model.AppError { 75 if jobError == nil { 76 result := <-srv.Store.Job().UpdateStatus(job.Id, model.JOB_STATUS_ERROR) 77 return result.Err 78 } 79 80 job.Status = model.JOB_STATUS_ERROR 81 job.Progress = -1 82 if job.Data == nil { 83 job.Data = make(map[string]string) 84 } 85 job.Data["error"] = jobError.Message + " — " + jobError.DetailedError 86 87 if result := <-srv.Store.Job().UpdateOptimistically(job, model.JOB_STATUS_IN_PROGRESS); result.Err != nil { 88 return result.Err 89 } else { 90 if !result.Data.(bool) { 91 if result := <-srv.Store.Job().UpdateOptimistically(job, model.JOB_STATUS_CANCEL_REQUESTED); result.Err != nil { 92 return result.Err 93 } else { 94 if !result.Data.(bool) { 95 return model.NewAppError("Jobs.SetJobError", "jobs.set_job_error.update.error", nil, "id="+job.Id, http.StatusInternalServerError) 96 } 97 } 98 } 99 } 100 101 return nil 102 } 103 104 func (srv *JobServer) SetJobCanceled(job *model.Job) *model.AppError { 105 result := <-srv.Store.Job().UpdateStatus(job.Id, model.JOB_STATUS_CANCELED) 106 return result.Err 107 } 108 109 func (srv *JobServer) UpdateInProgressJobData(job *model.Job) *model.AppError { 110 job.Status = model.JOB_STATUS_IN_PROGRESS 111 job.LastActivityAt = model.GetMillis() 112 result := <-srv.Store.Job().UpdateOptimistically(job, model.JOB_STATUS_IN_PROGRESS) 113 return result.Err 114 } 115 116 func (srv *JobServer) RequestCancellation(jobId string) *model.AppError { 117 if result := <-srv.Store.Job().UpdateStatusOptimistically(jobId, model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED); result.Err != nil { 118 return result.Err 119 } else if result.Data.(bool) { 120 return nil 121 } 122 123 if result := <-srv.Store.Job().UpdateStatusOptimistically(jobId, model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_CANCEL_REQUESTED); result.Err != nil { 124 return result.Err 125 } else if result.Data.(bool) { 126 return nil 127 } 128 129 return model.NewAppError("Jobs.RequestCancellation", "jobs.request_cancellation.status.error", nil, "id="+jobId, http.StatusInternalServerError) 130 } 131 132 func (srv *JobServer) CancellationWatcher(ctx context.Context, jobId string, cancelChan chan interface{}) { 133 for { 134 select { 135 case <-ctx.Done(): 136 mlog.Debug(fmt.Sprintf("CancellationWatcher for Job: %v Aborting as job has finished.", jobId)) 137 return 138 case <-time.After(CANCEL_WATCHER_POLLING_INTERVAL * time.Millisecond): 139 mlog.Debug(fmt.Sprintf("CancellationWatcher for Job: %v polling.", jobId)) 140 if result := <-srv.Store.Job().Get(jobId); result.Err == nil { 141 jobStatus := result.Data.(*model.Job) 142 if jobStatus.Status == model.JOB_STATUS_CANCEL_REQUESTED { 143 close(cancelChan) 144 return 145 } 146 } 147 } 148 } 149 } 150 151 func GenerateNextStartDateTime(now time.Time, nextStartTime time.Time) *time.Time { 152 nextTime := time.Date(now.Year(), now.Month(), now.Day(), nextStartTime.Hour(), nextStartTime.Minute(), 0, 0, time.Local) 153 154 if !now.Before(nextTime) { 155 nextTime = nextTime.AddDate(0, 0, 1) 156 } 157 158 return &nextTime 159 } 160 161 func (srv *JobServer) CheckForPendingJobsByType(jobType string) (bool, *model.AppError) { 162 if result := <-srv.Store.Job().GetCountByStatusAndType(model.JOB_STATUS_PENDING, jobType); result.Err != nil { 163 return false, result.Err 164 } else { 165 return result.Data.(int64) > 0, nil 166 } 167 } 168 169 func (srv *JobServer) GetLastSuccessfulJobByType(jobType string) (*model.Job, *model.AppError) { 170 if result := <-srv.Store.Job().GetNewestJobByStatusAndType(model.JOB_STATUS_SUCCESS, jobType); result.Err != nil { 171 return nil, result.Err 172 } else { 173 return result.Data.(*model.Job), nil 174 } 175 }