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.

146 lines
4.0 KiB

  1. package shell
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "github.com/chrislusf/seaweedfs/weed/filer"
  7. "github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
  8. "github.com/chrislusf/seaweedfs/weed/util"
  9. "io"
  10. )
  11. func init() {
  12. Commands = append(Commands, &commandRemoteUnmount{})
  13. }
  14. type commandRemoteUnmount struct {
  15. }
  16. func (c *commandRemoteUnmount) Name() string {
  17. return "remote.unmount"
  18. }
  19. func (c *commandRemoteUnmount) Help() string {
  20. return `unmount remote storage
  21. # assume a remote storage is configured to name "s3_1"
  22. remote.configure -name=s3_1 -type=s3 -access_key=xxx -secret_key=yyy
  23. # mount and pull one bucket
  24. remote.mount -dir=/xxx -remote=s3_1/bucket
  25. # unmount the mounted directory and remove its cache
  26. # Make sure you have stopped "weed filer.remote.sync" first!
  27. # Otherwise, the deletion will also be propagated to the remote storage!!!
  28. remote.unmount -dir=/xxx -iHaveStoppedRemoteSync
  29. `
  30. }
  31. func (c *commandRemoteUnmount) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {
  32. remoteMountCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
  33. dir := remoteMountCommand.String("dir", "", "a directory in filer")
  34. hasStoppedRemoteSync := remoteMountCommand.Bool("iHaveStoppedRemoteSync", false, "confirm to stop weed filer.remote.sync first")
  35. if err = remoteMountCommand.Parse(args); err != nil {
  36. return nil
  37. }
  38. mappings, listErr := filer.ReadMountMappings(commandEnv.option.GrpcDialOption, commandEnv.option.FilerAddress)
  39. if listErr != nil {
  40. return listErr
  41. }
  42. if *dir == "" {
  43. return jsonPrintln(writer, mappings)
  44. }
  45. _, found := mappings.Mappings[*dir]
  46. if !found {
  47. return fmt.Errorf("directory %s is not mounted", *dir)
  48. }
  49. if !*hasStoppedRemoteSync {
  50. return fmt.Errorf("make sure \"weed filer.remote.sync\" is stopped to avoid data loss")
  51. }
  52. // purge mounted data
  53. if err = c.purgeMountedData(commandEnv, *dir); err != nil {
  54. return fmt.Errorf("purge mounted data: %v", err)
  55. }
  56. // store a mount configuration in filer
  57. if err = c.deleteMountMapping(commandEnv, *dir); err != nil {
  58. return fmt.Errorf("delete mount mapping: %v", err)
  59. }
  60. return nil
  61. }
  62. func (c *commandRemoteUnmount) purgeMountedData(commandEnv *CommandEnv, dir string) error {
  63. // find existing directory, and ensure the directory is empty
  64. err := commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  65. parent, name := util.FullPath(dir).DirAndName()
  66. lookupResp, lookupErr := client.LookupDirectoryEntry(context.Background(), &filer_pb.LookupDirectoryEntryRequest{
  67. Directory: parent,
  68. Name: name,
  69. })
  70. if lookupErr != nil {
  71. return fmt.Errorf("lookup %s: %v", dir, lookupErr)
  72. }
  73. oldEntry := lookupResp.Entry
  74. deleteError := filer_pb.DoRemove(client, parent, name, true, true, true, false, nil)
  75. if deleteError != nil {
  76. return fmt.Errorf("delete %s: %v", dir, deleteError)
  77. }
  78. mkdirErr := filer_pb.DoMkdir(client, parent, name, func(entry *filer_pb.Entry) {
  79. entry.Attributes = oldEntry.Attributes
  80. entry.Extended = oldEntry.Extended
  81. })
  82. if mkdirErr != nil {
  83. return fmt.Errorf("mkdir %s: %v", dir, mkdirErr)
  84. }
  85. return nil
  86. })
  87. if err != nil {
  88. return err
  89. }
  90. return nil
  91. }
  92. func (c *commandRemoteUnmount) deleteMountMapping(commandEnv *CommandEnv, dir string) (err error) {
  93. // read current mapping
  94. var oldContent, newContent []byte
  95. err = commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  96. oldContent, err = filer.ReadInsideFiler(client, filer.DirectoryEtcRemote, filer.REMOTE_STORAGE_MOUNT_FILE)
  97. return err
  98. })
  99. if err != nil {
  100. if err != filer_pb.ErrNotFound {
  101. return fmt.Errorf("read existing mapping: %v", err)
  102. }
  103. }
  104. // add new mapping
  105. newContent, err = filer.RemoveRemoteStorageMapping(oldContent, dir)
  106. if err != nil {
  107. return fmt.Errorf("delete mount %s: %v", dir, err)
  108. }
  109. // save back
  110. err = commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
  111. return filer.SaveInsideFiler(client, filer.DirectoryEtcRemote, filer.REMOTE_STORAGE_MOUNT_FILE, newContent)
  112. })
  113. if err != nil {
  114. return fmt.Errorf("save mapping: %v", err)
  115. }
  116. return nil
  117. }