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.

206 lines
4.4 KiB

6 years ago
6 years ago
6 years ago
4 years ago
6 years ago
6 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
6 years ago
6 years ago
6 years ago
  1. package shell
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/seaweedfs/seaweedfs/weed/cluster"
  6. "github.com/seaweedfs/seaweedfs/weed/pb"
  7. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  8. "github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
  9. "github.com/seaweedfs/seaweedfs/weed/util/grace"
  10. "golang.org/x/exp/slices"
  11. "io"
  12. "math/rand"
  13. "os"
  14. "path"
  15. "regexp"
  16. "strings"
  17. "github.com/peterh/liner"
  18. )
  19. var (
  20. line *liner.State
  21. historyPath = path.Join(os.TempDir(), "weed-shell")
  22. )
  23. func RunShell(options ShellOptions) {
  24. slices.SortFunc(Commands, func(a, b command) bool {
  25. return strings.Compare(a.Name(), b.Name()) < 0
  26. })
  27. line = liner.NewLiner()
  28. defer line.Close()
  29. grace.OnInterrupt(func() {
  30. line.Close()
  31. })
  32. line.SetCtrlCAborts(true)
  33. line.SetTabCompletionStyle(liner.TabPrints)
  34. setCompletionHandler()
  35. loadHistory()
  36. defer saveHistory()
  37. reg, _ := regexp.Compile(`'.*?'|".*?"|\S+`)
  38. commandEnv := NewCommandEnv(&options)
  39. go commandEnv.MasterClient.KeepConnectedToMaster()
  40. commandEnv.MasterClient.WaitUntilConnected()
  41. if commandEnv.option.FilerAddress == "" {
  42. var filers []pb.ServerAddress
  43. commandEnv.MasterClient.WithClient(false, func(client master_pb.SeaweedClient) error {
  44. resp, err := client.ListClusterNodes(context.Background(), &master_pb.ListClusterNodesRequest{
  45. ClientType: cluster.FilerType,
  46. FilerGroup: *options.FilerGroup,
  47. })
  48. if err != nil {
  49. return err
  50. }
  51. for _, clusterNode := range resp.ClusterNodes {
  52. filers = append(filers, pb.ServerAddress(clusterNode.Address))
  53. }
  54. return nil
  55. })
  56. fmt.Printf("master: %s ", *options.Masters)
  57. if len(filers) > 0 {
  58. fmt.Printf("filers: %v", filers)
  59. commandEnv.option.FilerAddress = filers[rand.Intn(len(filers))]
  60. }
  61. fmt.Println()
  62. }
  63. if commandEnv.option.FilerAddress != "" {
  64. commandEnv.WithFilerClient(false, func(filerClient filer_pb.SeaweedFilerClient) error {
  65. resp, err := filerClient.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
  66. if err != nil {
  67. return err
  68. }
  69. if resp.ClusterId != "" {
  70. fmt.Printf(`
  71. ---
  72. Free Monitoring Data URL:
  73. https://cloud.seaweedfs.com/ui/%s
  74. ---
  75. `, resp.ClusterId)
  76. }
  77. return nil
  78. })
  79. }
  80. for {
  81. cmd, err := line.Prompt("> ")
  82. if err != nil {
  83. if err != io.EOF {
  84. fmt.Printf("%v\n", err)
  85. }
  86. return
  87. }
  88. for _, c := range strings.Split(cmd, ";") {
  89. if processEachCmd(reg, c, commandEnv) {
  90. return
  91. }
  92. }
  93. }
  94. }
  95. func processEachCmd(reg *regexp.Regexp, cmd string, commandEnv *CommandEnv) bool {
  96. cmds := reg.FindAllString(cmd, -1)
  97. line.AppendHistory(cmd)
  98. if len(cmds) == 0 {
  99. return false
  100. } else {
  101. args := make([]string, len(cmds[1:]))
  102. for i := range args {
  103. args[i] = strings.Trim(string(cmds[1+i]), "\"'")
  104. }
  105. cmd := cmds[0]
  106. if cmd == "help" || cmd == "?" {
  107. printHelp(cmds)
  108. } else if cmd == "exit" || cmd == "quit" {
  109. return true
  110. } else {
  111. foundCommand := false
  112. for _, c := range Commands {
  113. if c.Name() == cmd || c.Name() == "fs."+cmd {
  114. if err := c.Do(args, commandEnv, os.Stdout); err != nil {
  115. fmt.Fprintf(os.Stderr, "error: %v\n", err)
  116. }
  117. foundCommand = true
  118. }
  119. }
  120. if !foundCommand {
  121. fmt.Fprintf(os.Stderr, "unknown command: %v\n", cmd)
  122. }
  123. }
  124. }
  125. return false
  126. }
  127. func printGenericHelp() {
  128. msg :=
  129. `Type: "help <command>" for help on <command>. Most commands support "<command> -h" also for options.
  130. `
  131. fmt.Print(msg)
  132. for _, c := range Commands {
  133. helpTexts := strings.SplitN(c.Help(), "\n", 2)
  134. fmt.Printf(" %-30s\t# %s \n", c.Name(), helpTexts[0])
  135. }
  136. }
  137. func printHelp(cmds []string) {
  138. args := cmds[1:]
  139. if len(args) == 0 {
  140. printGenericHelp()
  141. } else if len(args) > 1 {
  142. fmt.Println()
  143. } else {
  144. cmd := strings.ToLower(args[0])
  145. for _, c := range Commands {
  146. if c.Name() == cmd {
  147. fmt.Printf(" %s\t# %s\n", c.Name(), c.Help())
  148. }
  149. }
  150. }
  151. }
  152. func setCompletionHandler() {
  153. line.SetCompleter(func(line string) (c []string) {
  154. for _, i := range Commands {
  155. if strings.HasPrefix(i.Name(), strings.ToLower(line)) {
  156. c = append(c, i.Name())
  157. }
  158. }
  159. return
  160. })
  161. }
  162. func loadHistory() {
  163. if f, err := os.Open(historyPath); err == nil {
  164. line.ReadHistory(f)
  165. f.Close()
  166. }
  167. }
  168. func saveHistory() {
  169. if f, err := os.Create(historyPath); err != nil {
  170. fmt.Printf("Error creating history file: %v\n", err)
  171. } else {
  172. if _, err = line.WriteHistory(f); err != nil {
  173. fmt.Printf("Error writing history file: %v\n", err)
  174. }
  175. f.Close()
  176. }
  177. }