Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 38 additions & 23 deletions cmd/minikube/cmd/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ var supportedFilesystems = map[string]bool{nineP: true}

// mountCmd represents the mount command
var mountCmd = &cobra.Command{
Use: "mount [flags] <source directory>:<target directory>",
Short: "Mounts the specified directory into minikube",
Long: `Mounts the specified directory into minikube.`,
Use: "mount [flags] <source directory>:<target directory>[;<source directory>:<target directory>;...]",
Short: "Mounts the specified directories into minikube",
Long: `Mounts the specified directories into minikube.`,
Example: "minikube mount /hostdir1:/vmdir1\nminikube mount \"/hostdir1:/vmdir1;/hostdir2:/vmdir2\"",
Run: func(_ *cobra.Command, args []string) {
if isKill {
if err := killMountProcess(); err != nil {
Expand All @@ -104,22 +105,36 @@ var mountCmd = &cobra.Command{
minikube mount <source directory>:<target directory> (example: "/host-home:/vm-home")`)
}
mountString := args[0]
idx := strings.LastIndex(mountString, ":")
if idx == -1 { // no ":" was present
exit.Message(reason.Usage, `mount argument "{{.value}}" must be in form: <source directory>:<target directory>`, out.V{"value": mountString})
}
hostPath := mountString[:idx]
vmPath := mountString[idx+1:]
if _, err := os.Stat(hostPath); err != nil {
if os.IsNotExist(err) {
exit.Message(reason.HostPathMissing, "Cannot find directory {{.path}} for mount", out.V{"path": hostPath})
} else {
exit.Error(reason.HostPathStat, "stat failed", err)
var hostPaths []string
var vmPaths []string
vmPathMap := make(map[string]string)
for _, item := range strings.Split(mountString, ";") {
paths := strings.Split(item, ":")
// no ":" was present
if len(paths) != 2 {
exit.Message(reason.Usage, `mount argument "{{.value}}" must be in form: <source directory>:<target directory>[;<source directory>:<target directory>;...]`, out.V{"value": mountString})
}

// check host and vm path
if _, err := os.Stat(paths[0]); err != nil {
if os.IsNotExist(err) {
exit.Message(reason.HostPathMissing, "Cannot find directory {{.path}} for mount", out.V{"path": paths[0]})
} else {
exit.Error(reason.HostPathStat, "stat failed", err)
}
}
if len(paths[1]) == 0 || !strings.HasPrefix(paths[1], "/") {
exit.Message(reason.Usage, "Target directory {{.path}} must be an absolute path", out.V{"path": paths[1]})
}
if _, exists := vmPathMap[paths[1]]; exists {
exit.Message(reason.Usage, "Target directory {{.path}} must be unique", out.V{"path": paths[1]})
}
vmPathMap[paths[1]] = paths[0]

hostPaths = append(hostPaths, paths[0])
vmPaths = append(vmPaths, paths[1])
}
if len(vmPath) == 0 || !strings.HasPrefix(vmPath, "/") {
exit.Message(reason.Usage, "Target directory {{.path}} must be an absolute path", out.V{"path": vmPath})
}

var debugVal int
if klog.V(1).Enabled() {
debugVal = 1 // ufs.StartServer takes int debug param
Expand Down Expand Up @@ -200,7 +215,7 @@ var mountCmd = &cobra.Command{
if driver.IsKIC(co.CP.Host.Driver.DriverName()) && runtime.GOOS != "linux" {
bindIP = "127.0.0.1"
}
out.Step(style.Mounting, "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.Step(style.Mounting, "Mounting host path {{.sourcePath}} into VM as {{.destinationPath}} ...", out.V{"sourcePath": hostPaths, "destinationPath": vmPaths})
out.Infof("Mount type: {{.name}}", out.V{"name": cfg.Type})
out.Infof("User ID: {{.userID}}", out.V{"userID": cfg.UID})
out.Infof("Group ID: {{.groupID}}", out.V{"groupID": cfg.GID})
Expand All @@ -216,7 +231,7 @@ var mountCmd = &cobra.Command{
go func(pid chan int) {
pid <- os.Getpid()
out.Styled(style.Fileserver, "Userspace file server: ")
ufs.StartServer(net.JoinHostPort(bindIP, strconv.Itoa(port)), debugVal, hostPath)
ufs.StartServer(net.JoinHostPort(bindIP, strconv.Itoa(port)), debugVal, hostPaths)
out.Step(style.Stopped, "Userspace file server is shutdown")
wg.Done()
}(pidchan)
Expand All @@ -228,8 +243,8 @@ var mountCmd = &cobra.Command{
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
for sig := range c {
out.Step(style.Unmount, "Unmounting {{.path}} ...", out.V{"path": vmPath})
err := cluster.Unmount(co.CP.Runner, vmPath)
out.Step(style.Unmount, "Unmounting {{.path}} ...", out.V{"path": vmPaths})
err := cluster.Unmount(co.CP.Runner, vmPaths)
if err != nil {
out.FailureT("Failed unmount: {{.error}}", out.V{"error": err})
}
Expand All @@ -243,14 +258,14 @@ var mountCmd = &cobra.Command{
}
}()

err = cluster.Mount(co.CP.Runner, ip.String(), vmPath, cfg, pid)
err = cluster.Mount(co.CP.Runner, ip.String(), vmPaths, cfg, pid)
if err != nil {
if rtErr, ok := err.(*cluster.MountError); ok && rtErr.ErrorType == cluster.MountErrorConnect {
exit.Error(reason.GuestMountCouldNotConnect, "mount could not connect", rtErr)
}
exit.Error(reason.GuestMount, "mount failed", err)
}
out.Step(style.Success, "Successfully mounted {{.sourcePath}} to {{.destinationPath}}", out.V{"sourcePath": hostPath, "destinationPath": vmPath})
out.Step(style.Success, "Successfully mounted {{.sourcePath}} to {{.destinationPath}}", out.V{"sourcePath": hostPaths, "destinationPath": vmPaths})
out.Ln("")
out.Styled(style.Notice, "NOTE: This process must stay alive for the mount to be accessible ...")
wg.Wait()
Expand Down
31 changes: 25 additions & 6 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,34 @@ func runStart(cmd *cobra.Command, _ []string) {
validateBuiltImageVersion(starter.Runner, ds.Name)

if existing != nil && driver.IsKIC(existing.Driver) && viper.GetBool(createMount) {
old := ""
if len(existing.ContainerVolumeMounts) > 0 {
old = existing.ContainerVolumeMounts[0]
isSameMount := func(oldm, newm []string) bool {
if len(oldm) != len(newm) {
return false
}

mountmap := make(map[string]int)
for _, item := range oldm {
mountmap[item]++
}

for _, item := range newm {
cnt, exists := mountmap[item]
if !exists || cnt == 0 {
return false
}
mountmap[item]--
}

return true
}
if mount := viper.GetString(mountString); old != mount {

oldMounts := existing.ContainerVolumeMounts
newMounts := getMountStrings()
if !isSameMount(oldMounts, newMounts) {
exit.Message(reason.GuestMountConflict, "Sorry, {{.driver}} does not allow mounts to be changed after container creation (previous mount: '{{.old}}', new mount: '{{.new}})'", out.V{
"driver": existing.Driver,
"new": mount,
"old": old,
"new": newMounts,
"old": oldMounts,
})
}
}
Expand Down
31 changes: 27 additions & 4 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func initMinikubeFlags() {
startCmd.Flags().Bool(embedCerts, false, "if true, will embed the certs in kubeconfig.")
startCmd.Flags().StringP(containerRuntime, "c", constants.DefaultContainerRuntime, fmt.Sprintf("The container runtime to be used. Valid options: %s (default: auto)", strings.Join(cruntime.ValidRuntimes(), ", ")))
startCmd.Flags().Bool(createMount, false, "This will start the mount daemon and automatically mount files into minikube.")
startCmd.Flags().String(mountString, constants.DefaultMountDir+":/minikube-host", "The argument to pass the minikube mount command on start.")
startCmd.Flags().String(mountString, constants.DefaultMountDir+":/minikube-host", "The argument to pass the minikube mount command on start, in a semicolon-separated format. (eg. /foo:/foo;/bar:/bar:Z,rshared)")
startCmd.Flags().String(mount9PVersion, defaultMount9PVersion, mount9PVersionDescription)
startCmd.Flags().String(mountGID, defaultMountGID, mountGIDDescription)
startCmd.Flags().String(mountIPFlag, defaultMountIP, mountIPDescription)
Expand Down Expand Up @@ -492,6 +492,15 @@ func getNetwork(driverName string) string {
return n
}

func getMountStrings() []string {
mountStrings := strings.Split(viper.GetString(mountString), ";")
if !validateMountStrings(mountStrings) {
exit.Message(reason.Usage, "The mount-string contains duplicate items.")
}

return mountStrings
}

func validateQemuNetwork(n string) string {
switch n {
case "socket_vmnet":
Expand Down Expand Up @@ -546,6 +555,17 @@ func validateVfkitNetwork(n string) string {
return n
}

func validateMountStrings(mountStrings []string) bool {
mountmap := make(map[string]bool)
for _, mountstring := range mountStrings {
if _, exists := mountmap[mountstring]; exists {
return false
}
mountmap[mountstring] = true
}
return true
}

// generateNewConfigFromFlags generate a config.ClusterConfig based on flags
func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime string, drvName string) config.ClusterConfig {
var cc config.ClusterConfig
Expand Down Expand Up @@ -613,7 +633,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
ExtraDisks: viper.GetInt(extraDisks),
CertExpiration: viper.GetDuration(certExpiration),
Mount: viper.GetBool(createMount),
MountString: viper.GetString(mountString),
MountStrings: getMountStrings(),
Mount9PVersion: viper.GetString(mount9PVersion),
MountGID: viper.GetString(mountGID),
MountIP: viper.GetString(mountIPFlag),
Expand Down Expand Up @@ -653,7 +673,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
}
cc.VerifyComponents = interpretWaitFlag(*cmd)
if viper.GetBool(createMount) && driver.IsKIC(drvName) {
cc.ContainerVolumeMounts = []string{viper.GetString(mountString)}
cc.ContainerVolumeMounts = getMountStrings()
}

if driver.IsKIC(drvName) {
Expand Down Expand Up @@ -694,6 +714,10 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
}
}

if cmd.Flags().Changed(mountString) {
cc.MountStrings = getMountStrings()
}

return cc
}

Expand Down Expand Up @@ -865,7 +889,6 @@ func updateExistingConfigFromFlags(cmd *cobra.Command, existing *config.ClusterC
updateBoolFromFlag(cmd, &cc.KubernetesConfig.ShouldLoadCachedImages, cacheImages)
updateDurationFromFlag(cmd, &cc.CertExpiration, certExpiration)
updateBoolFromFlag(cmd, &cc.Mount, createMount)
updateStringFromFlag(cmd, &cc.MountString, mountString)
updateStringFromFlag(cmd, &cc.Mount9PVersion, mount9PVersion)
updateStringFromFlag(cmd, &cc.MountGID, mountGID)
updateStringFromFlag(cmd, &cc.MountIP, mountIPFlag)
Expand Down
Loading