cal_diff.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "os/exec"
  8. "strings"
  9. "time"
  10. "go-common/app/job/main/appstatic/model"
  11. "go-common/library/log"
  12. )
  13. const (
  14. errFormat = "Func:[%s] - Step:[%s] - Error:[%v]"
  15. _zipType = "application/zip"
  16. _tmpSlash = "*"
  17. // file type
  18. _fullPackage = 0
  19. _diffPackge = 1
  20. _calculationInProgress = 2
  21. )
  22. // Sync modified season data to the license owner
  23. func (s *Service) calDiffproc() {
  24. defer s.waiter.Done()
  25. var (
  26. file *model.ResourceFile
  27. err error
  28. )
  29. for {
  30. if s.daoClosed {
  31. log.Info("DB closed!")
  32. return
  33. }
  34. time.Sleep(time.Duration(s.c.Cfg.Diff.FreDiff))
  35. // check recently created files
  36. if file, err = s.dao.DiffNew(ctx); err != nil {
  37. log.Error(errFormat, "callDiffproc", "diffNew", err)
  38. continue
  39. }
  40. // if recently created file not found, we retry failed files
  41. if file == nil {
  42. if file, err = s.dao.DiffRetry(ctx); err != nil {
  43. log.Error(errFormat, "callDiffproc", "DiffRetry", err)
  44. continue
  45. }
  46. if file == nil {
  47. log.Error(errFormat, "callDiffproc", "DiffRetry", "No Diff To Calculate")
  48. continue
  49. }
  50. }
  51. // begin calculate
  52. log.Info("Calculate Diff for (fileID:%d, ResID: %d, FileName:%s)", file.ID, file.ResourceID, file.Name)
  53. if err := s.calDiff(file); err != nil {
  54. log.Error(errFormat, "calDiffproc", "calDiff", err)
  55. continue
  56. }
  57. }
  58. }
  59. // get the file info from DB and download the file to local
  60. func (s *Service) catchFile(file *model.ResourceFile, version int) (newPath string, err error) {
  61. var (
  62. size int64
  63. newPkg *model.ResourceFile
  64. )
  65. // get the new package info
  66. if newPkg, err = s.getFile(file.ResourceID, _fullPackage, version); err != nil {
  67. log.Error(errFormat, "calDiff", fmt.Sprintf("getFileNew %v", file), err)
  68. s.chgFStatus(file.ID, _diffPackge)
  69. return
  70. }
  71. // save the new package
  72. newPkg.Name = strings.Replace(newPkg.Name, "/", _tmpSlash, 1) // avoid open file error
  73. newPath = fmt.Sprintf("%s/%s", s.c.Cfg.Diff.Folder, newPkg.Name) // combine the local path
  74. if size, err = s.dao.DownloadFile(ctx, newPkg.URL, newPath); err != nil {
  75. log.Error(errFormat, "calDiff", fmt.Sprintf("saveFile %v", file), err)
  76. s.chgFStatus(file.ID, _diffPackge)
  77. return
  78. }
  79. // check result file
  80. if _, err = os.Stat(newPath); os.IsNotExist(err) {
  81. log.Error(errFormat, "diffCmd", "IsNotExist", newPath+" - File Not Exist")
  82. return
  83. }
  84. log.Info("Save File From URL [%s] in [%s], Size: %d", newPkg.URL, newPath, size)
  85. return
  86. }
  87. // calculate the diff for one file struct and upload the result to BFS and fill the URL
  88. func (s *Service) calDiff(file *model.ResourceFile) (err error) {
  89. var (
  90. newPath string
  91. oldPath string
  92. patchPath string
  93. patchURL string
  94. patchFInfo *model.FileInfo
  95. )
  96. // update the status of the file, to avoid being picked by another
  97. if err = s.chgFStatus(file.ID, _calculationInProgress); err != nil {
  98. log.Error(errFormat, "calDiff", "chgFStatus", err)
  99. return
  100. }
  101. // save the new file
  102. if newPath, err = s.catchFile(file, 0); err != nil {
  103. log.Error(errFormat, "callDiff", "catchFile", err)
  104. return
  105. }
  106. // save the old file
  107. if oldPath, err = s.catchFile(file, int(file.FromVer)); err != nil {
  108. log.Error(errFormat, "callDiff", "catchFile", err)
  109. return
  110. }
  111. // exec bsdiff to get the patch file and upload it
  112. file.Name = strings.Replace(file.Name, "/", _tmpSlash, 1)
  113. patchPath = s.c.Cfg.Diff.Folder + "/" + file.Name
  114. if patchFInfo, patchURL, err = s.diffCmd(file.Name, patchPath, newPath, oldPath); err != nil {
  115. log.Error(errFormat, "calDiff", "diffCmd", err)
  116. s.chgFStatus(file.ID, _diffPackge)
  117. return
  118. }
  119. log.Info("Upload Path File From [%s] to [%s], Size: %d", patchPath, patchURL)
  120. // save the url to the file
  121. if err = s.dao.SaveFile(ctx, file.ID, &model.FileInfo{
  122. Name: patchFInfo.Name,
  123. Size: patchFInfo.Size,
  124. Md5: patchFInfo.Md5,
  125. URL: patchURL}); err != nil {
  126. log.Error(errFormat, "calDiff", "updateURL", err)
  127. return
  128. }
  129. // delete all the packages used
  130. if err = delPkgs(newPath, oldPath, patchPath); err != nil {
  131. log.Error(errFormat, "calDiff", "delPkgs", err)
  132. }
  133. return
  134. }
  135. // delete all the packages used to generate the diff pkg
  136. func delPkgs(newPath string, oldPath string, patchPath string) (err error) {
  137. if err = deleteFile(newPath); err != nil {
  138. log.Error(errFormat, "delPkgs", "NewPath", err)
  139. return
  140. }
  141. if err = deleteFile(oldPath); err != nil {
  142. log.Error(errFormat, "delPkgs", "oldPath", err)
  143. return
  144. }
  145. if err = deleteFile(patchPath); err != nil {
  146. log.Error(errFormat, "delPkgs", "patchPath", err)
  147. }
  148. return
  149. }
  150. // delete one file
  151. func deleteFile(path string) (err error) {
  152. // check file
  153. if _, err = os.Stat(path); os.IsNotExist(err) {
  154. err = fmt.Errorf("File %s Not exist", path)
  155. log.Error(errFormat, "deleteFile", "IsNotExist", "File Not Exist")
  156. return
  157. }
  158. if err = os.Remove(path); err != nil {
  159. log.Error(errFormat, "deleteFile", "Remove", err)
  160. return
  161. }
  162. log.Info("Delete %s Succesfully", path)
  163. return
  164. }
  165. // execute the bsdiff command to have the result
  166. func (s *Service) diffCmd(patchName string, patchPath string, newPath string, oldPath string) (patchFInfo *model.FileInfo, location string, err error) {
  167. var (
  168. content []byte
  169. fInfo os.FileInfo
  170. )
  171. begin := time.Now()
  172. cmd := exec.Command("bsdiff", oldPath, newPath, patchPath)
  173. // exec Command
  174. if err = cmd.Run(); err != nil {
  175. log.Error(errFormat, "diffCmd", "cmdRun", err)
  176. return
  177. }
  178. timeCost := time.Since(begin)
  179. log.Info("BSDiff Command Finished. Time Cost: %v, Cmd:%s %s %s %s", timeCost, "bsdiff", oldPath, newPath, patchPath)
  180. // check patch file
  181. if fInfo, err = os.Stat(patchPath); os.IsNotExist(err) {
  182. log.Error(errFormat, "diffCmd", "patchFileCheck", "File Not Exist")
  183. return
  184. }
  185. log.Info("Patch File Generated, Name: %s, Size: %s.", fInfo.Name(), fInfo.Size())
  186. // read patch file and upload
  187. if content, err = ioutil.ReadFile(patchPath); err != nil {
  188. log.Error(errFormat, "diffCmd", "ReadFile_Patch", err)
  189. return
  190. }
  191. if patchFInfo, err = s.ParseFile(content); err != nil {
  192. log.Error(errFormat, "diffCmd", "ParsePatchFile", err)
  193. return
  194. }
  195. patchFInfo.Name = rename(patchName, patchFInfo.Md5)
  196. // upload patch file to bfs
  197. location, err = s.Upload(context.Background(), patchFInfo.Name, _zipType, time.Now().Unix(), content)
  198. if err != nil {
  199. log.Error(errFormat, "diffCmd", "UploadPatch", err)
  200. }
  201. return
  202. }
  203. // split the patchName, pick the ModID (res[0]), VersionInfo (res[1]), Insert the md5 inside
  204. func rename(patchName string, md5 string) (newName string) {
  205. res := strings.Split(patchName, _tmpSlash)
  206. if len(res) != 2 {
  207. log.Error("patchName %s can't split", patchName)
  208. return patchName
  209. }
  210. return res[0] + "_" + md5 + "/" + res[1]
  211. }
  212. // get file object ( struct )
  213. func (s *Service) getFile(resID int, fileType int, version int) (file *model.ResourceFile, err error) {
  214. file = &model.ResourceFile{}
  215. var (
  216. res *model.Resource // current version
  217. resHis *model.Resource // history version
  218. poolID int
  219. )
  220. if res, err = s.dao.ParseResID(ctx, resID); err != nil {
  221. log.Error("[getFile]-[findPool %d]-Error(%v)", resID, err)
  222. return
  223. }
  224. poolID = int(res.PoolID)
  225. if version != 0 { // full pkg of the history version
  226. if resHis, err = s.dao.ParseResVer(ctx, poolID, version); err != nil {
  227. log.Error("[getFile]-[findVersion]-Error(%v)", err)
  228. return
  229. }
  230. resID = int(resHis.ID)
  231. }
  232. if file, err = s.dao.ReadyFile(ctx, resID, fileType); err != nil {
  233. log.Error(errFormat, "getUrl", "First", err)
  234. }
  235. return
  236. }
  237. // change the file's status
  238. func (s *Service) chgFStatus(fileID int, status int) (err error) {
  239. if err = s.dao.UpdateStatus(ctx, status, fileID); err != nil {
  240. log.Error(errFormat, "chgFStatus", "update", err)
  241. }
  242. return
  243. }