github.com/pachyderm/pachyderm@v1.13.4/src/server/pfs/cmds/mount_unix.go (about)

     1  // +build !windows
     2  
     3  package cmds
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"fmt"
     9  	"os"
    10  	"strings"
    11  	"syscall"
    12  
    13  	"github.com/pachyderm/pachyderm/src/client"
    14  	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
    15  	"github.com/pachyderm/pachyderm/src/server/pfs/fuse"
    16  	"github.com/pachyderm/pachyderm/src/server/pkg/cmdutil"
    17  
    18  	"github.com/hanwen/go-fuse/v2/fs"
    19  	gofuse "github.com/hanwen/go-fuse/v2/fuse"
    20  	"github.com/spf13/cobra"
    21  )
    22  
    23  const (
    24  	name = "pfs"
    25  )
    26  
    27  func parseRepoOpts(args []string) (map[string]*fuse.RepoOptions, error) {
    28  	result := make(map[string]*fuse.RepoOptions)
    29  	for _, arg := range args {
    30  		var repo string
    31  		var flag string
    32  		opts := &fuse.RepoOptions{}
    33  		repoAndRest := strings.Split(arg, "@")
    34  		if len(repoAndRest) == 1 {
    35  			// No branch specified
    36  			opts.Branch = "master"
    37  			repoAndFlag := strings.Split(repoAndRest[0], "+")
    38  			repo = repoAndFlag[0]
    39  			if len(repoAndFlag) > 1 {
    40  				flag = repoAndFlag[1]
    41  			}
    42  		} else {
    43  			repo = repoAndRest[0]
    44  			branchAndFlag := strings.Split(repoAndRest[1], "+")
    45  			opts.Branch = branchAndFlag[0]
    46  			if len(branchAndFlag) > 1 {
    47  				flag = branchAndFlag[1]
    48  			}
    49  		}
    50  		if flag != "" {
    51  			for _, c := range flag {
    52  				if c != 'w' && c != 'r' {
    53  					return nil, errors.Errorf("invalid format %q: unrecognized mode: %q", arg, c)
    54  				}
    55  			}
    56  			if strings.Contains("w", flag) {
    57  				opts.Write = true
    58  			}
    59  		}
    60  		if repo == "" {
    61  			return nil, errors.Errorf("invalid format %q: repo cannot be empty", arg)
    62  		}
    63  		result[repo] = opts
    64  	}
    65  	return result, nil
    66  }
    67  
    68  func mountCmds() []*cobra.Command {
    69  	var commands []*cobra.Command
    70  
    71  	var write bool
    72  	var debug bool
    73  	var repoOpts cmdutil.RepeatedStringArg
    74  	mount := &cobra.Command{
    75  		Use:   "{{alias}} <path/to/mount/point>",
    76  		Short: "Mount pfs locally. This command blocks.",
    77  		Long:  "Mount pfs locally. This command blocks.",
    78  		Run: cmdutil.RunFixedArgs(1, func(args []string) error {
    79  			c, err := client.NewOnUserMachine("fuse")
    80  			if err != nil {
    81  				return err
    82  			}
    83  			defer c.Close()
    84  			mountPoint := args[0]
    85  			repoOpts, err := parseRepoOpts(repoOpts)
    86  			if err != nil {
    87  				return err
    88  			}
    89  			opts := &fuse.Options{
    90  				Write: write,
    91  				Fuse: &fs.Options{
    92  					MountOptions: gofuse.MountOptions{
    93  						Debug:  debug,
    94  						FsName: name,
    95  						Name:   name,
    96  					},
    97  				},
    98  				RepoOptions: repoOpts,
    99  			}
   100  			// Prints a warning if we're on macOS
   101  			printWarning()
   102  			return fuse.Mount(c, mountPoint, opts)
   103  		}),
   104  	}
   105  	mount.Flags().BoolVarP(&write, "write", "w", false, "Allow writing to pfs through the mount.")
   106  	mount.Flags().BoolVarP(&debug, "debug", "d", false, "Turn on debug messages.")
   107  	mount.Flags().VarP(&repoOpts, "repos", "r", "Repos and branches / commits to mount, arguments should be of the form \"repo@branch+w\", where the trailing flag \"+w\" indicates write.")
   108  	mount.MarkFlagCustom("repos", "__pachctl_get_repo_branch")
   109  	commands = append(commands, cmdutil.CreateAlias(mount, "mount"))
   110  
   111  	var all bool
   112  	unmount := &cobra.Command{
   113  		Use:   "{{alias}} <path/to/mount/point>",
   114  		Short: "Unmount pfs.",
   115  		Long:  "Unmount pfs.",
   116  		Run: cmdutil.RunBoundedArgs(0, 1, func(args []string) error {
   117  			if len(args) == 1 {
   118  				return syscall.Unmount(args[0], 0)
   119  			}
   120  			if all {
   121  				stdin := strings.NewReader(fmt.Sprintf(`
   122  		mount | grep fuse.%s | cut -f 3 -d " "
   123  		`, name))
   124  				var stdout bytes.Buffer
   125  				if err := cmdutil.RunIO(cmdutil.IO{
   126  					Stdin:  stdin,
   127  					Stdout: &stdout,
   128  					Stderr: os.Stderr,
   129  				}, "sh"); err != nil {
   130  					return err
   131  				}
   132  				scanner := bufio.NewScanner(&stdout)
   133  				var mounts []string
   134  				for scanner.Scan() {
   135  					mounts = append(mounts, scanner.Text())
   136  				}
   137  				if len(mounts) == 0 {
   138  					fmt.Println("No mounts found.")
   139  					return nil
   140  				}
   141  				fmt.Printf("This will unmount the following filesystems:\n")
   142  				for _, mount := range mounts {
   143  					fmt.Printf("%s\n", mount)
   144  				}
   145  
   146  				if ok, err := cmdutil.InteractiveConfirm(); err != nil {
   147  					return err
   148  				} else if !ok {
   149  					return errors.New("deploy aborted")
   150  				}
   151  
   152  				for _, mount := range mounts {
   153  					if err := syscall.Unmount(mount, 0); err != nil {
   154  						return err
   155  					}
   156  				}
   157  			} else {
   158  				return errors.Errorf("nothing to unmount specify a mounted filesystem or --all")
   159  			}
   160  			return nil
   161  		}),
   162  	}
   163  	unmount.Flags().BoolVarP(&all, "all", "a", false, "unmount all pfs mounts")
   164  	commands = append(commands, cmdutil.CreateAlias(unmount, "unmount"))
   165  
   166  	return commands
   167  }