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

145 lines
5.0 KiB

3 years ago
3 years ago
3 years ago
  1. package command
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/chrislusf/seaweedfs/weed/glog"
  6. "github.com/chrislusf/seaweedfs/weed/pb"
  7. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  8. "github.com/chrislusf/seaweedfs/weed/pb/remote_pb"
  9. "github.com/chrislusf/seaweedfs/weed/replication/source"
  10. "github.com/chrislusf/seaweedfs/weed/security"
  11. "github.com/chrislusf/seaweedfs/weed/util"
  12. "google.golang.org/grpc"
  13. "os"
  14. "time"
  15. )
  16. type RemoteSyncOptions struct {
  17. filerAddress *string
  18. grpcDialOption grpc.DialOption
  19. readChunkFromFiler *bool
  20. debug *bool
  21. timeAgo *time.Duration
  22. dir *string
  23. createBucketAt *string
  24. createBucketRandomSuffix *bool
  25. mappings *remote_pb.RemoteStorageMapping
  26. remoteConfs map[string]*remote_pb.RemoteConf
  27. bucketsDir string
  28. }
  29. var _ = filer_pb.FilerClient(&RemoteSyncOptions{})
  30. func (option *RemoteSyncOptions) WithFilerClient(fn func(filer_pb.SeaweedFilerClient) error) error {
  31. return pb.WithFilerClient(*option.filerAddress, option.grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
  32. return fn(client)
  33. })
  34. }
  35. func (option *RemoteSyncOptions) AdjustedUrl(location *filer_pb.Location) string {
  36. return location.Url
  37. }
  38. var (
  39. remoteSyncOptions RemoteSyncOptions
  40. )
  41. func init() {
  42. cmdFilerRemoteSynchronize.Run = runFilerRemoteSynchronize // break init cycle
  43. remoteSyncOptions.filerAddress = cmdFilerRemoteSynchronize.Flag.String("filer", "localhost:8888", "filer of the SeaweedFS cluster")
  44. remoteSyncOptions.dir = cmdFilerRemoteSynchronize.Flag.String("dir", "", "a mounted directory on filer")
  45. remoteSyncOptions.createBucketAt = cmdFilerRemoteSynchronize.Flag.String("createBucketAt", "", "one remote storage name to create new buckets in")
  46. remoteSyncOptions.createBucketRandomSuffix = cmdFilerRemoteSynchronize.Flag.Bool("createBucketWithRandomSuffix", true, "add randomized suffix to bucket name to avoid conflicts")
  47. remoteSyncOptions.readChunkFromFiler = cmdFilerRemoteSynchronize.Flag.Bool("filerProxy", false, "read file chunks from filer instead of volume servers")
  48. remoteSyncOptions.debug = cmdFilerRemoteSynchronize.Flag.Bool("debug", false, "debug mode to print out filer updated remote files")
  49. remoteSyncOptions.timeAgo = cmdFilerRemoteSynchronize.Flag.Duration("timeAgo", 0, "start time before now. \"300ms\", \"1.5h\" or \"2h45m\". Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\"")
  50. }
  51. var cmdFilerRemoteSynchronize = &Command{
  52. UsageLine: "filer.remote.sync",
  53. Short: "resumable continuously write back updates to remote storage",
  54. Long: `resumable continuously write back updates to remote storage
  55. filer.remote.sync listens on filer update events.
  56. If any mounted remote file is updated, it will fetch the updated content,
  57. and write to the remote storage.
  58. There are two modes:
  59. 1)By default, watch /buckets folder and write back all changes.
  60. # if there is only one remote storage configured
  61. weed filer.remote.sync
  62. # if there are multiple remote storages configured
  63. # specify a remote storage to create new buckets.
  64. weed filer.remote.sync -createBucketAt=cloud1
  65. 2)Write back one mounted folder to remote storage
  66. weed filer.remote.sync -dir=/mount/s3_on_cloud
  67. `,
  68. }
  69. func runFilerRemoteSynchronize(cmd *Command, args []string) bool {
  70. util.LoadConfiguration("security", false)
  71. grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")
  72. remoteSyncOptions.grpcDialOption = grpcDialOption
  73. dir := *remoteSyncOptions.dir
  74. filerAddress := *remoteSyncOptions.filerAddress
  75. filerSource := &source.FilerSource{}
  76. filerSource.DoInitialize(
  77. filerAddress,
  78. pb.ServerToGrpcAddress(filerAddress),
  79. "/", // does not matter
  80. *remoteSyncOptions.readChunkFromFiler,
  81. )
  82. remoteSyncOptions.bucketsDir = "/buckets"
  83. // check buckets again
  84. remoteSyncOptions.WithFilerClient(func(filerClient filer_pb.SeaweedFilerClient) error {
  85. resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
  86. if err != nil {
  87. return err
  88. }
  89. remoteSyncOptions.bucketsDir = resp.DirBuckets
  90. return nil
  91. })
  92. if dir != "" && dir != remoteSyncOptions.bucketsDir {
  93. fmt.Printf("synchronize %s to remote storage...\n", dir)
  94. util.RetryForever("filer.remote.sync "+dir, func() error {
  95. return followUpdatesAndUploadToRemote(&remoteSyncOptions, filerSource, dir)
  96. }, func(err error) bool {
  97. if err != nil {
  98. glog.Errorf("synchronize %s: %v", dir, err)
  99. }
  100. return true
  101. })
  102. return true
  103. }
  104. // read filer remote storage mount mappings
  105. if detectErr := remoteSyncOptions.collectRemoteStorageConf(); detectErr != nil {
  106. fmt.Fprintf(os.Stderr, "read mount info: %v\n", detectErr)
  107. return true
  108. }
  109. // synchronize /buckets folder
  110. fmt.Printf("synchronize buckets in %s ...\n", remoteSyncOptions.bucketsDir)
  111. util.RetryForever("filer.remote.sync buckets", func() error {
  112. return remoteSyncOptions.followBucketUpdatesAndUploadToRemote(filerSource)
  113. }, func(err error) bool {
  114. if err != nil {
  115. glog.Errorf("synchronize %s: %v", remoteSyncOptions.bucketsDir, err)
  116. }
  117. return true
  118. })
  119. return true
  120. }