You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

storage.go 9.3 kB

2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package mq
  2. import (
  3. "fmt"
  4. "io/fs"
  5. "os"
  6. "path/filepath"
  7. "strconv"
  8. "time"
  9. "github.com/samber/lo"
  10. "gitlink.org.cn/cloudream/common/consts/errorcode"
  11. "gitlink.org.cn/cloudream/common/pkgs/logger"
  12. "gitlink.org.cn/cloudream/common/pkgs/mq"
  13. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  14. mytask "gitlink.org.cn/cloudream/storage/agent/internal/task"
  15. "gitlink.org.cn/cloudream/storage/common/consts"
  16. stgglb "gitlink.org.cn/cloudream/storage/common/globals"
  17. "gitlink.org.cn/cloudream/storage/common/pkgs/db2/model"
  18. "gitlink.org.cn/cloudream/storage/common/pkgs/iterator"
  19. agtmq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/agent"
  20. coormq "gitlink.org.cn/cloudream/storage/common/pkgs/mq/coordinator"
  21. "gitlink.org.cn/cloudream/storage/common/utils"
  22. )
  23. func (svc *Service) StartStorageLoadPackage(msg *agtmq.StartStorageLoadPackage) (*agtmq.StartStorageLoadPackageResp, *mq.CodeMessage) {
  24. tsk := svc.taskManager.StartNew(mytask.NewStorageLoadPackage(msg.UserID, msg.PackageID, msg.StorageID))
  25. return mq.ReplyOK(agtmq.NewStartStorageLoadPackageResp(tsk.ID()))
  26. }
  27. func (svc *Service) WaitStorageLoadPackage(msg *agtmq.WaitStorageLoadPackage) (*agtmq.WaitStorageLoadPackageResp, *mq.CodeMessage) {
  28. logger.WithField("TaskID", msg.TaskID).Debugf("wait loading package")
  29. tsk := svc.taskManager.FindByID(msg.TaskID)
  30. if tsk == nil {
  31. return nil, mq.Failed(errorcode.TaskNotFound, "task not found")
  32. }
  33. if msg.WaitTimeoutMs == 0 {
  34. tsk.Wait()
  35. errMsg := ""
  36. if tsk.Error() != nil {
  37. errMsg = tsk.Error().Error()
  38. }
  39. loadTsk := tsk.Body().(*mytask.StorageLoadPackage)
  40. return mq.ReplyOK(agtmq.NewWaitStorageLoadPackageResp(true, errMsg, loadTsk.PackagePath, loadTsk.LocalBase, loadTsk.RemoteBase))
  41. } else {
  42. if tsk.WaitTimeout(time.Duration(msg.WaitTimeoutMs) * time.Millisecond) {
  43. errMsg := ""
  44. if tsk.Error() != nil {
  45. errMsg = tsk.Error().Error()
  46. }
  47. loadTsk := tsk.Body().(*mytask.StorageLoadPackage)
  48. return mq.ReplyOK(agtmq.NewWaitStorageLoadPackageResp(true, errMsg, loadTsk.PackagePath, loadTsk.LocalBase, loadTsk.RemoteBase))
  49. }
  50. return mq.ReplyOK(agtmq.NewWaitStorageLoadPackageResp(false, "", "", "", ""))
  51. }
  52. }
  53. func (svc *Service) StorageCheck(msg *agtmq.StorageCheck) (*agtmq.StorageCheckResp, *mq.CodeMessage) {
  54. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  55. if err != nil {
  56. return mq.ReplyOK(agtmq.NewStorageCheckResp(
  57. err.Error(),
  58. nil,
  59. ))
  60. }
  61. defer stgglb.CoordinatorMQPool.Release(coorCli)
  62. // TODO UserID。应该设计两种接口,一种需要UserID,一种不需要。
  63. getStg, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails([]cdssdk.StorageID{msg.StorageID}))
  64. if err != nil {
  65. return mq.ReplyOK(agtmq.NewStorageCheckResp(
  66. err.Error(),
  67. nil,
  68. ))
  69. }
  70. if getStg.Storages[0] == nil {
  71. return nil, mq.Failed(errorcode.OperationFailed, "storage not found")
  72. }
  73. if getStg.Storages[0].Shared == nil {
  74. return nil, mq.Failed(errorcode.OperationFailed, "storage has no shared storage")
  75. }
  76. entries, err := os.ReadDir(utils.MakeStorageLoadDirectory(getStg.Storages[0].Shared.LoadBase))
  77. if err != nil {
  78. logger.Warnf("list storage directory failed, err: %s", err.Error())
  79. return mq.ReplyOK(agtmq.NewStorageCheckResp(
  80. err.Error(),
  81. nil,
  82. ))
  83. }
  84. var stgPkgs []model.StoragePackage
  85. userDirs := lo.Filter(entries, func(info fs.DirEntry, index int) bool { return info.IsDir() })
  86. for _, dir := range userDirs {
  87. userIDInt, err := strconv.ParseInt(dir.Name(), 10, 64)
  88. if err != nil {
  89. logger.Warnf("parsing user id %s: %s", dir.Name(), err.Error())
  90. continue
  91. }
  92. pkgDir := filepath.Join(utils.MakeStorageLoadDirectory(getStg.Storages[0].Shared.LoadBase), dir.Name())
  93. pkgDirs, err := os.ReadDir(pkgDir)
  94. if err != nil {
  95. logger.Warnf("reading package dir %s: %s", pkgDir, err.Error())
  96. continue
  97. }
  98. for _, pkg := range pkgDirs {
  99. pkgIDInt, err := strconv.ParseInt(pkg.Name(), 10, 64)
  100. if err != nil {
  101. logger.Warnf("parsing package dir %s: %s", pkg.Name(), err.Error())
  102. continue
  103. }
  104. stgPkgs = append(stgPkgs, model.StoragePackage{
  105. StorageID: msg.StorageID,
  106. PackageID: cdssdk.PackageID(pkgIDInt),
  107. UserID: cdssdk.UserID(userIDInt),
  108. })
  109. }
  110. }
  111. return mq.ReplyOK(agtmq.NewStorageCheckResp(consts.StorageDirectoryStateOK, stgPkgs))
  112. }
  113. func (svc *Service) StorageGC(msg *agtmq.StorageGC) (*agtmq.StorageGCResp, *mq.CodeMessage) {
  114. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  115. if err != nil {
  116. return nil, mq.Failed(errorcode.OperationFailed, err.Error())
  117. }
  118. defer stgglb.CoordinatorMQPool.Release(coorCli)
  119. // TODO UserID。应该设计两种接口,一种需要UserID,一种不需要。
  120. getStg, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails([]cdssdk.StorageID{msg.StorageID}))
  121. if err != nil {
  122. return nil, mq.Failed(errorcode.OperationFailed, err.Error())
  123. }
  124. if getStg.Storages[0] == nil {
  125. return nil, mq.Failed(errorcode.OperationFailed, "storage not found")
  126. }
  127. if getStg.Storages[0].Shared == nil {
  128. return nil, mq.Failed(errorcode.OperationFailed, "storage has no shared storage")
  129. }
  130. entries, err := os.ReadDir(utils.MakeStorageLoadDirectory(getStg.Storages[0].Shared.LoadBase))
  131. if err != nil {
  132. logger.Warnf("list storage directory failed, err: %s", err.Error())
  133. return nil, mq.Failed(errorcode.OperationFailed, "list directory files failed")
  134. }
  135. // userID->pkgID->pkg
  136. userPkgs := make(map[string]map[string]bool)
  137. for _, pkg := range msg.Packages {
  138. userIDStr := fmt.Sprintf("%d", pkg.UserID)
  139. pkgs, ok := userPkgs[userIDStr]
  140. if !ok {
  141. pkgs = make(map[string]bool)
  142. userPkgs[userIDStr] = pkgs
  143. }
  144. pkgIDStr := fmt.Sprintf("%d", pkg.PackageID)
  145. pkgs[pkgIDStr] = true
  146. }
  147. userDirs := lo.Filter(entries, func(info fs.DirEntry, index int) bool { return info.IsDir() })
  148. for _, dir := range userDirs {
  149. pkgMap, ok := userPkgs[dir.Name()]
  150. // 第一级目录名是UserID,先删除UserID在StoragePackage表里没出现过的文件夹
  151. if !ok {
  152. rmPath := filepath.Join(utils.MakeStorageLoadDirectory(getStg.Storages[0].Shared.LoadBase), dir.Name())
  153. err := os.RemoveAll(rmPath)
  154. if err != nil {
  155. logger.Warnf("removing user dir %s: %s", rmPath, err.Error())
  156. } else {
  157. logger.Debugf("user dir %s removed by gc", rmPath)
  158. }
  159. continue
  160. }
  161. pkgDir := filepath.Join(utils.MakeStorageLoadDirectory(getStg.Storages[0].Shared.LoadBase), dir.Name())
  162. // 遍历每个UserID目录的packages目录里的内容
  163. pkgs, err := os.ReadDir(pkgDir)
  164. if err != nil {
  165. logger.Warnf("reading package dir %s: %s", pkgDir, err.Error())
  166. continue
  167. }
  168. for _, pkg := range pkgs {
  169. if !pkgMap[pkg.Name()] {
  170. rmPath := filepath.Join(pkgDir, pkg.Name())
  171. err := os.RemoveAll(rmPath)
  172. if err != nil {
  173. logger.Warnf("removing package dir %s: %s", rmPath, err.Error())
  174. } else {
  175. logger.Debugf("package dir %s removed by gc", rmPath)
  176. }
  177. }
  178. }
  179. }
  180. return mq.ReplyOK(agtmq.RespStorageGC())
  181. }
  182. func (svc *Service) StartStorageCreatePackage(msg *agtmq.StartStorageCreatePackage) (*agtmq.StartStorageCreatePackageResp, *mq.CodeMessage) {
  183. coorCli, err := stgglb.CoordinatorMQPool.Acquire()
  184. if err != nil {
  185. logger.Warnf("new coordinator client: %s", err.Error())
  186. return nil, mq.Failed(errorcode.OperationFailed, "new coordinator client failed")
  187. }
  188. defer stgglb.CoordinatorMQPool.Release(coorCli)
  189. getStg, err := coorCli.GetStorageDetails(coormq.ReqGetStorageDetails([]cdssdk.StorageID{msg.StorageID}))
  190. if err != nil {
  191. return nil, mq.Failed(errorcode.OperationFailed, err.Error())
  192. }
  193. if getStg.Storages[0] == nil {
  194. return nil, mq.Failed(errorcode.OperationFailed, "storage not found")
  195. }
  196. if getStg.Storages[0].Shared == nil {
  197. return nil, mq.Failed(errorcode.OperationFailed, "storage has no shared storage")
  198. }
  199. fullPath := filepath.Clean(filepath.Join(getStg.Storages[0].Shared.LoadBase, msg.Path))
  200. var uploadFilePathes []string
  201. err = filepath.WalkDir(fullPath, func(fname string, fi os.DirEntry, err error) error {
  202. if err != nil {
  203. return nil
  204. }
  205. if !fi.IsDir() {
  206. uploadFilePathes = append(uploadFilePathes, fname)
  207. }
  208. return nil
  209. })
  210. if err != nil {
  211. logger.Warnf("opening directory %s: %s", fullPath, err.Error())
  212. return nil, mq.Failed(errorcode.OperationFailed, "read directory failed")
  213. }
  214. objIter := iterator.NewUploadingObjectIterator(fullPath, uploadFilePathes)
  215. tsk := svc.taskManager.StartNew(mytask.NewCreatePackage(msg.UserID, msg.BucketID, msg.Name, objIter, msg.StorageAffinity))
  216. return mq.ReplyOK(agtmq.NewStartStorageCreatePackageResp(tsk.ID()))
  217. }
  218. func (svc *Service) WaitStorageCreatePackage(msg *agtmq.WaitStorageCreatePackage) (*agtmq.WaitStorageCreatePackageResp, *mq.CodeMessage) {
  219. tsk := svc.taskManager.FindByID(msg.TaskID)
  220. if tsk == nil {
  221. return nil, mq.Failed(errorcode.TaskNotFound, "task not found")
  222. }
  223. if msg.WaitTimeoutMs == 0 {
  224. tsk.Wait()
  225. } else if !tsk.WaitTimeout(time.Duration(msg.WaitTimeoutMs) * time.Millisecond) {
  226. return mq.ReplyOK(agtmq.NewWaitStorageCreatePackageResp(false, "", 0))
  227. }
  228. if tsk.Error() != nil {
  229. return mq.ReplyOK(agtmq.NewWaitStorageCreatePackageResp(true, tsk.Error().Error(), 0))
  230. }
  231. taskBody := tsk.Body().(*mytask.CreatePackage)
  232. return mq.ReplyOK(agtmq.NewWaitStorageCreatePackageResp(true, "", taskBody.Result.PackageID))
  233. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。