github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/jobs/import_delete/worker.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package import_delete 5 6 import ( 7 "errors" 8 "path/filepath" 9 "time" 10 11 "github.com/mattermost/mattermost-server/v5/app" 12 "github.com/mattermost/mattermost-server/v5/jobs" 13 tjobs "github.com/mattermost/mattermost-server/v5/jobs/interfaces" 14 "github.com/mattermost/mattermost-server/v5/mlog" 15 "github.com/mattermost/mattermost-server/v5/model" 16 "github.com/mattermost/mattermost-server/v5/store" 17 ) 18 19 func init() { 20 app.RegisterJobsImportDeleteInterface(func(a *app.App) tjobs.ImportDeleteInterface { 21 return &ImportDeleteInterfaceImpl{a} 22 }) 23 } 24 25 type ImportDeleteInterfaceImpl struct { 26 app *app.App 27 } 28 29 type ImportDeleteWorker struct { 30 name string 31 stopChan chan struct{} 32 stoppedChan chan struct{} 33 jobsChan chan model.Job 34 jobServer *jobs.JobServer 35 app *app.App 36 } 37 38 func (i *ImportDeleteInterfaceImpl) MakeWorker() model.Worker { 39 return &ImportDeleteWorker{ 40 name: "ImportDelete", 41 stopChan: make(chan struct{}), 42 stoppedChan: make(chan struct{}), 43 jobsChan: make(chan model.Job), 44 jobServer: i.app.Srv().Jobs, 45 app: i.app, 46 } 47 } 48 49 func (w *ImportDeleteWorker) JobChannel() chan<- model.Job { 50 return w.jobsChan 51 } 52 53 func (w *ImportDeleteWorker) Run() { 54 mlog.Debug("Worker started", mlog.String("worker", w.name)) 55 56 defer func() { 57 mlog.Debug("Worker finished", mlog.String("worker", w.name)) 58 close(w.stoppedChan) 59 }() 60 61 for { 62 select { 63 case <-w.stopChan: 64 mlog.Debug("Worker received stop signal", mlog.String("worker", w.name)) 65 return 66 case job := <-w.jobsChan: 67 mlog.Debug("Worker received a new candidate job.", mlog.String("worker", w.name)) 68 w.doJob(&job) 69 } 70 } 71 } 72 73 func (w *ImportDeleteWorker) Stop() { 74 mlog.Debug("Worker stopping", mlog.String("worker", w.name)) 75 close(w.stopChan) 76 <-w.stoppedChan 77 } 78 79 func (w *ImportDeleteWorker) doJob(job *model.Job) { 80 if claimed, err := w.jobServer.ClaimJob(job); err != nil { 81 mlog.Warn("Worker experienced an error while trying to claim job", 82 mlog.String("worker", w.name), 83 mlog.String("job_id", job.Id), 84 mlog.String("error", err.Error())) 85 return 86 } else if !claimed { 87 return 88 } 89 90 importPath := *w.app.Config().ImportSettings.Directory 91 retentionTime := time.Duration(*w.app.Config().ImportSettings.RetentionDays) * 24 * time.Hour 92 imports, appErr := w.app.ListDirectory(importPath) 93 if appErr != nil { 94 w.setJobError(job, appErr) 95 return 96 } 97 98 var hasErrs bool 99 for i := range imports { 100 filename := filepath.Base(imports[i]) 101 modTime, appErr := w.app.FileModTime(filepath.Join(importPath, filename)) 102 if appErr != nil { 103 mlog.Debug("Worker: Failed to get file modification time", 104 mlog.Err(appErr), mlog.String("import", imports[i])) 105 hasErrs = true 106 continue 107 } 108 109 if time.Now().After(modTime.Add(retentionTime)) { 110 // expected format if uploaded through the API is 111 // ${uploadID}_${filename}${app.IncompleteUploadSuffix} 112 minLen := 26 + 1 + len(app.IncompleteUploadSuffix) 113 114 // check if it's an incomplete upload and attempt to delete its session. 115 if len(filename) > minLen && filepath.Ext(filename) == app.IncompleteUploadSuffix { 116 uploadID := filename[:26] 117 if storeErr := w.app.Srv().Store.UploadSession().Delete(uploadID); storeErr != nil { 118 mlog.Debug("Worker: Failed to delete UploadSession", 119 mlog.Err(storeErr), mlog.String("upload_id", uploadID)) 120 hasErrs = true 121 continue 122 } 123 } else { 124 // check if fileinfo exists and if so delete it. 125 filePath := filepath.Join(imports[i]) 126 info, storeErr := w.app.Srv().Store.FileInfo().GetByPath(filePath) 127 var nfErr *store.ErrNotFound 128 if storeErr != nil && !errors.As(storeErr, &nfErr) { 129 mlog.Debug("Worker: Failed to get FileInfo", 130 mlog.Err(storeErr), mlog.String("path", filePath)) 131 hasErrs = true 132 continue 133 } else if storeErr == nil { 134 if storeErr = w.app.Srv().Store.FileInfo().PermanentDelete(info.Id); storeErr != nil { 135 mlog.Debug("Worker: Failed to delete FileInfo", 136 mlog.Err(storeErr), mlog.String("file_id", info.Id)) 137 hasErrs = true 138 continue 139 } 140 } 141 } 142 143 // remove file data from storage. 144 if appErr := w.app.RemoveFile(imports[i]); appErr != nil { 145 mlog.Debug("Worker: Failed to remove file", 146 mlog.Err(appErr), mlog.String("import", imports[i])) 147 hasErrs = true 148 continue 149 } 150 } 151 } 152 153 if hasErrs { 154 mlog.Warn("Worker: errors occurred") 155 } 156 157 mlog.Info("Worker: Job is complete", mlog.String("worker", w.name), mlog.String("job_id", job.Id)) 158 w.setJobSuccess(job) 159 } 160 161 func (w *ImportDeleteWorker) setJobSuccess(job *model.Job) { 162 if err := w.app.Srv().Jobs.SetJobSuccess(job); err != nil { 163 mlog.Error("Worker: Failed to set success for job", mlog.String("worker", w.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 164 w.setJobError(job, err) 165 } 166 } 167 168 func (w *ImportDeleteWorker) setJobError(job *model.Job, appError *model.AppError) { 169 if err := w.app.Srv().Jobs.SetJobError(job, appError); err != nil { 170 mlog.Error("Worker: Failed to set job error", mlog.String("worker", w.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 171 } 172 }