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.

s2s.go 4.3 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package obs
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "time"
  7. "github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
  8. oms "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/oms/v2"
  9. "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/oms/v2/model"
  10. omsregion "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/oms/v2/region"
  11. "gitlink.org.cn/cloudream/common/utils/os2"
  12. clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
  13. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/s3"
  14. "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/types"
  15. cortypes "gitlink.org.cn/cloudream/jcs-pub/coordinator/types"
  16. )
  17. type S2STransfer struct {
  18. stgType *cortypes.OBSType
  19. cred *cortypes.OBSCred
  20. feat *cortypes.S2STransferFeature
  21. taskID *int64
  22. omsCli *oms.OmsClient
  23. }
  24. func NewS2STransfer(stgType *cortypes.OBSType, cred *cortypes.OBSCred, feat *cortypes.S2STransferFeature) *S2STransfer {
  25. return &S2STransfer{
  26. stgType: stgType,
  27. cred: cred,
  28. feat: feat,
  29. }
  30. }
  31. // 判断是否能从指定的源存储中直传到当前存储的目的路径
  32. func (s *S2STransfer) CanTransfer(src *clitypes.UserSpaceDetail) bool {
  33. req := s.makeRequest(src, "")
  34. return req != nil
  35. }
  36. // 执行数据直传。返回传输后的文件路径
  37. func (s *S2STransfer) Transfer(ctx context.Context, src *clitypes.UserSpaceDetail, srcPath string, opt types.S2SOption) (string, error) {
  38. req := s.makeRequest(src, srcPath)
  39. if req == nil {
  40. return "", fmt.Errorf("unsupported source storage type: %T", src.UserSpace.Storage)
  41. }
  42. auth, err := basic.NewCredentialsBuilder().
  43. WithAk(s.cred.AK).
  44. WithSk(s.cred.SK).
  45. WithProjectId(s.stgType.ProjectID).
  46. SafeBuild()
  47. if err != nil {
  48. return "", err
  49. }
  50. region, err := omsregion.SafeValueOf(s.stgType.Region)
  51. if err != nil {
  52. return "", err
  53. }
  54. cli, err := oms.OmsClientBuilder().
  55. WithRegion(region).
  56. WithCredential(auth).
  57. SafeBuild()
  58. if err != nil {
  59. return "", err
  60. }
  61. tempPrefix := s3.JoinKey(s.feat.TempDir, os2.GenerateRandomFileName(10)) + "/"
  62. taskType := model.GetCreateTaskReqTaskTypeEnum().OBJECT
  63. s.omsCli = oms.NewOmsClient(cli)
  64. resp, err := s.omsCli.CreateTask(&model.CreateTaskRequest{
  65. Body: &model.CreateTaskReq{
  66. TaskType: &taskType,
  67. SrcNode: req,
  68. DstNode: &model.DstNodeReq{
  69. Region: s.stgType.Region,
  70. Ak: s.cred.AK,
  71. Sk: s.cred.SK,
  72. Bucket: s.stgType.Bucket,
  73. SavePrefix: &tempPrefix,
  74. },
  75. },
  76. })
  77. if err != nil {
  78. return "", fmt.Errorf("create task: %w", err)
  79. }
  80. s.taskID = resp.Id
  81. err = s.waitTask(ctx, *resp.Id)
  82. if err != nil {
  83. return "", fmt.Errorf("wait task: %w", err)
  84. }
  85. return s3.JoinKey(tempPrefix, srcPath), nil
  86. }
  87. func (s *S2STransfer) makeRequest(srcStg *clitypes.UserSpaceDetail, srcPath string) *model.SrcNodeReq {
  88. switch srcType := srcStg.UserSpace.Storage.(type) {
  89. case *cortypes.OBSType:
  90. cloudType := "HuaweiCloud"
  91. cred, ok := srcStg.UserSpace.Credential.(*cortypes.OBSCred)
  92. if !ok {
  93. return nil
  94. }
  95. return &model.SrcNodeReq{
  96. CloudType: &cloudType,
  97. Region: &srcType.Region,
  98. Ak: &cred.AK,
  99. Sk: &cred.SK,
  100. Bucket: &srcType.Bucket,
  101. ObjectKey: &[]string{srcPath},
  102. }
  103. default:
  104. return nil
  105. }
  106. }
  107. func (s *S2STransfer) waitTask(ctx context.Context, taskId int64) error {
  108. ticker := time.NewTicker(time.Second * 5)
  109. defer ticker.Stop()
  110. failures := 0
  111. for {
  112. resp, err := s.omsCli.ShowTask(&model.ShowTaskRequest{
  113. TaskId: fmt.Sprintf("%v", taskId),
  114. })
  115. if err != nil {
  116. if failures < 3 {
  117. failures++
  118. continue
  119. }
  120. return fmt.Errorf("show task failed too many times: %w", err)
  121. }
  122. failures = 0
  123. if *resp.Status == 3 {
  124. return fmt.Errorf("task stopped")
  125. }
  126. if *resp.Status == 4 {
  127. return errors.New(resp.ErrorReason.String())
  128. }
  129. if *resp.Status == 5 {
  130. return nil
  131. }
  132. select {
  133. case <-ticker.C:
  134. continue
  135. case <-ctx.Done():
  136. return ctx.Err()
  137. }
  138. }
  139. }
  140. // 完成传输
  141. func (s *S2STransfer) Complete() {
  142. }
  143. // 取消传输。如果已经调用了Complete,则这个方法应该无效果
  144. func (s *S2STransfer) Abort() {
  145. if s.taskID != nil {
  146. s.omsCli.StopTask(&model.StopTaskRequest{
  147. TaskId: fmt.Sprintf("%v", *s.taskID),
  148. })
  149. s.omsCli.DeleteTask(&model.DeleteTaskRequest{
  150. TaskId: fmt.Sprintf("%v", *s.taskID),
  151. })
  152. // TODO 清理临时文件
  153. }
  154. }

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