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.
		
		
		
		
		
			
		
			
				
					
					
						
							220 lines
						
					
					
						
							5.9 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							220 lines
						
					
					
						
							5.9 KiB
						
					
					
				
								package shell
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"context"
							 | 
						|
									"fmt"
							 | 
						|
									"io"
							 | 
						|
									"net/url"
							 | 
						|
									"strconv"
							 | 
						|
									"strings"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/operation"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/volume_server_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/storage/needle_map"
							 | 
						|
								
							 | 
						|
									"google.golang.org/grpc"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/wdclient"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/wdclient/exclusive_locks"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type ShellOptions struct {
							 | 
						|
									Masters        *string
							 | 
						|
									GrpcDialOption grpc.DialOption
							 | 
						|
									// shell transient context
							 | 
						|
									FilerHost    string
							 | 
						|
									FilerPort    int64
							 | 
						|
									FilerGroup   *string
							 | 
						|
									FilerAddress pb.ServerAddress
							 | 
						|
									Directory    string
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type CommandEnv struct {
							 | 
						|
									env          map[string]string
							 | 
						|
									MasterClient *wdclient.MasterClient
							 | 
						|
									option       *ShellOptions
							 | 
						|
									locker       *exclusive_locks.ExclusiveLocker
							 | 
						|
									noLock       bool
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func NewCommandEnv(options *ShellOptions) *CommandEnv {
							 | 
						|
									ce := &CommandEnv{
							 | 
						|
										env:          make(map[string]string),
							 | 
						|
										MasterClient: wdclient.NewMasterClient(options.GrpcDialOption, *options.FilerGroup, pb.AdminShellClient, "", "", "", *pb.ServerAddresses(*options.Masters).ToServiceDiscovery()),
							 | 
						|
										option:       options,
							 | 
						|
										noLock:       false,
							 | 
						|
									}
							 | 
						|
									ce.locker = exclusive_locks.NewExclusiveLocker(ce.MasterClient, "shell")
							 | 
						|
									return ce
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) parseUrl(input string) (path string, err error) {
							 | 
						|
									if strings.HasPrefix(input, "http") {
							 | 
						|
										err = fmt.Errorf("http://<filer>:<port> prefix is not supported any more")
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									if !strings.HasPrefix(input, "/") {
							 | 
						|
										input = util.Join(ce.option.Directory, input)
							 | 
						|
									}
							 | 
						|
									return input, err
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) isDirectory(path string) bool {
							 | 
						|
								
							 | 
						|
									return ce.checkDirectory(path) == nil
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) confirmIsLocked(args []string) error {
							 | 
						|
								
							 | 
						|
									if ce.locker.IsLocked() {
							 | 
						|
										return nil
							 | 
						|
									}
							 | 
						|
									ce.locker.SetMessage(fmt.Sprintf("%v", args))
							 | 
						|
								
							 | 
						|
									return fmt.Errorf("need to run \"lock\" first to continue")
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) isLocked() bool {
							 | 
						|
									if ce == nil {
							 | 
						|
										return true
							 | 
						|
									}
							 | 
						|
									if ce.noLock {
							 | 
						|
										return true
							 | 
						|
									}
							 | 
						|
									return ce.locker.IsLocked()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) checkDirectory(path string) error {
							 | 
						|
								
							 | 
						|
									dir, name := util.FullPath(path).DirAndName()
							 | 
						|
								
							 | 
						|
									exists, err := filer_pb.Exists(context.Background(), ce, dir, name, true)
							 | 
						|
								
							 | 
						|
									if !exists {
							 | 
						|
										return fmt.Errorf("%s is not a directory", path)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return err
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var _ = filer_pb.FilerClient(&CommandEnv{})
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) WithFilerClient(streamingMode bool, fn func(filer_pb.SeaweedFilerClient) error) error {
							 | 
						|
								
							 | 
						|
									return pb.WithGrpcFilerClient(streamingMode, 0, ce.option.FilerAddress, ce.option.GrpcDialOption, fn)
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) AdjustedUrl(location *filer_pb.Location) string {
							 | 
						|
									return location.Url
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (ce *CommandEnv) GetDataCenter() string {
							 | 
						|
									return ce.MasterClient.DataCenter
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func parseFilerUrl(entryPath string) (filerServer string, filerPort int64, path string, err error) {
							 | 
						|
									if strings.HasPrefix(entryPath, "http") {
							 | 
						|
										var u *url.URL
							 | 
						|
										u, err = url.Parse(entryPath)
							 | 
						|
										if err != nil {
							 | 
						|
											return
							 | 
						|
										}
							 | 
						|
										filerServer = u.Hostname()
							 | 
						|
										portString := u.Port()
							 | 
						|
										if portString != "" {
							 | 
						|
											filerPort, err = strconv.ParseInt(portString, 10, 32)
							 | 
						|
										}
							 | 
						|
										path = u.Path
							 | 
						|
									} else {
							 | 
						|
										err = fmt.Errorf("path should have full url /path/to/dirOrFile : %s", entryPath)
							 | 
						|
									}
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func findInputDirectory(args []string) (input string) {
							 | 
						|
									input = "."
							 | 
						|
									if len(args) > 0 {
							 | 
						|
										input = args[len(args)-1]
							 | 
						|
										if strings.HasPrefix(input, "-") {
							 | 
						|
											input = "."
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return input
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// isHelpRequest checks if the args contain a help flag (-h, --help, or -help)
							 | 
						|
								// It also handles combined short flags like -lh or -hl
							 | 
						|
								func isHelpRequest(args []string) bool {
							 | 
						|
									for _, arg := range args {
							 | 
						|
										// Check for exact matches
							 | 
						|
										if arg == "-h" || arg == "--help" || arg == "-help" {
							 | 
						|
											return true
							 | 
						|
										}
							 | 
						|
										// Check for combined short flags (e.g., -lh, -hl, -rfh)
							 | 
						|
										// Limit to reasonable length (2-4 chars total) to avoid matching long options like -verbose
							 | 
						|
										if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "--") && len(arg) > 1 && len(arg) <= 4 {
							 | 
						|
											for _, char := range arg[1:] {
							 | 
						|
												if char == 'h' {
							 | 
						|
													return true
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return false
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// handleHelpRequest checks for help flags and prints the help message if requested.
							 | 
						|
								// It returns true if the help message was printed, indicating the command should exit.
							 | 
						|
								func handleHelpRequest(c command, args []string, writer io.Writer) bool {
							 | 
						|
									if isHelpRequest(args) {
							 | 
						|
										fmt.Fprintln(writer, c.Help())
							 | 
						|
										return true
							 | 
						|
									}
							 | 
						|
									return false
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func readNeedleMeta(grpcDialOption grpc.DialOption, volumeServer pb.ServerAddress, volumeId uint32, needleValue needle_map.NeedleValue) (resp *volume_server_pb.ReadNeedleMetaResponse, err error) {
							 | 
						|
									err = operation.WithVolumeServerClient(false, volumeServer, grpcDialOption,
							 | 
						|
										func(client volume_server_pb.VolumeServerClient) error {
							 | 
						|
											if resp, err = client.ReadNeedleMeta(context.Background(), &volume_server_pb.ReadNeedleMetaRequest{
							 | 
						|
												VolumeId: volumeId,
							 | 
						|
												NeedleId: uint64(needleValue.Key),
							 | 
						|
												Offset:   needleValue.Offset.ToActualOffset(),
							 | 
						|
												Size:     int32(needleValue.Size),
							 | 
						|
											}); err != nil {
							 | 
						|
												return err
							 | 
						|
											}
							 | 
						|
											return nil
							 | 
						|
										},
							 | 
						|
									)
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func readNeedleStatus(grpcDialOption grpc.DialOption, sourceVolumeServer pb.ServerAddress, volumeId uint32, needleValue needle_map.NeedleValue) (resp *volume_server_pb.VolumeNeedleStatusResponse, err error) {
							 | 
						|
									err = operation.WithVolumeServerClient(false, sourceVolumeServer, grpcDialOption,
							 | 
						|
										func(client volume_server_pb.VolumeServerClient) error {
							 | 
						|
											if resp, err = client.VolumeNeedleStatus(context.Background(), &volume_server_pb.VolumeNeedleStatusRequest{
							 | 
						|
												VolumeId: volumeId,
							 | 
						|
												NeedleId: uint64(needleValue.Key),
							 | 
						|
											}); err != nil {
							 | 
						|
												return err
							 | 
						|
											}
							 | 
						|
											return nil
							 | 
						|
										},
							 | 
						|
									)
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func getCollectionName(commandEnv *CommandEnv, bucket string) string {
							 | 
						|
									if *commandEnv.option.FilerGroup != "" {
							 | 
						|
										return fmt.Sprintf("%s_%s", *commandEnv.option.FilerGroup, bucket)
							 | 
						|
									}
							 | 
						|
									return bucket
							 | 
						|
								}
							 |