Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
31 changes: 25 additions & 6 deletions cmd/minikube/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@
}

// runStart handles the executes the flow of "minikube start"
func runStart(cmd *cobra.Command, _ []string) {

Check failure on line 155 in cmd/minikube/cmd/start.go

View workflow job for this annotation

GitHub Actions / lint

cyclomatic complexity 32 of func `runStart` is high (> 30) (gocyclo)
register.SetEventLogPath(localpath.EventLog(ClusterFlagValue()))
ctx := context.Background()
out.SetJSON(outputFormat == "json")
Expand Down Expand Up @@ -269,15 +269,34 @@
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
172 changes: 107 additions & 65 deletions pkg/drivers/kic/oci/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,111 +17,153 @@ limitations under the License.
package oci

import (
"strings"
"testing"
)

func TestParseMountString(t *testing.T) {
testCases := []struct {
Name string
MountString string
MountStrings string
ExpectErr bool
ExpectedMount Mount
ExpectedMount []Mount
}{
{
Name: "basic linux",
MountString: "/foo:/bar",
ExpectErr: false,
ExpectedMount: Mount{
HostPath: "/foo",
ContainerPath: "/bar",
Name: "basic linux",
MountStrings: "/foo:/bar",
ExpectErr: false,
ExpectedMount: []Mount{
{
HostPath: "/foo",
ContainerPath: "/bar",
},
},
},
{
Name: "linux read only",
MountString: "/foo:/bar:ro",
ExpectErr: false,
ExpectedMount: Mount{
HostPath: "/foo",
ContainerPath: "/bar",
Readonly: true,
Name: "linux read only",
MountStrings: "/foo:/bar:ro",
ExpectErr: false,
ExpectedMount: []Mount{
{
HostPath: "/foo",
ContainerPath: "/bar",
Readonly: true,
},
},
},
{
Name: "windows style",
MountString: "C:\\Windows\\Path:/foo",
ExpectErr: false,
ExpectedMount: Mount{
HostPath: "C:\\Windows\\Path",
ContainerPath: "/foo",
Name: "windows style",
MountStrings: "C:\\Windows\\Path:/foo",
ExpectErr: false,
ExpectedMount: []Mount{
{
HostPath: "C:\\Windows\\Path",
ContainerPath: "/foo",
},
},
},
{
Name: "windows style read/write",
MountString: "C:\\Windows\\Path:/foo:rw",
ExpectErr: false,
ExpectedMount: Mount{
HostPath: "C:\\Windows\\Path",
ContainerPath: "/foo",
Readonly: false,
Name: "windows style read/write",
MountStrings: "C:\\Windows\\Path:/foo:rw",
ExpectErr: false,
ExpectedMount: []Mount{
{
HostPath: "C:\\Windows\\Path",
ContainerPath: "/foo",
Readonly: false,
},
},
},
{
Name: "container only",
MountString: "/foo",
ExpectErr: false,
ExpectedMount: Mount{
ContainerPath: "/foo",
Name: "container only",
MountStrings: "/foo",
ExpectErr: false,
ExpectedMount: []Mount{
{
ContainerPath: "/foo",
},
},
},
{
Name: "selinux relabel & bidirectional propagation",
MountString: "/foo:/bar/baz:Z,rshared",
ExpectErr: false,
ExpectedMount: Mount{
HostPath: "/foo",
ContainerPath: "/bar/baz",
SelinuxRelabel: true,
Propagation: MountPropagationBidirectional,
Name: "selinux relabel & bidirectional propagation",
MountStrings: "/foo:/bar/baz:Z,rshared",
ExpectErr: false,
ExpectedMount: []Mount{
{
HostPath: "/foo",
ContainerPath: "/bar/baz",
SelinuxRelabel: true,
Propagation: MountPropagationBidirectional,
},
},
},
{
Name: "invalid mount option",
MountString: "/foo:/bar:Z,bat",
ExpectErr: true,
ExpectedMount: Mount{
HostPath: "/foo",
ContainerPath: "/bar",
SelinuxRelabel: true,
Name: "invalid mount option",
MountStrings: "/foo:/bar:Z,bat",
ExpectErr: true,
ExpectedMount: []Mount{
{
HostPath: "/foo",
ContainerPath: "/bar",
SelinuxRelabel: true,
},
},
},
{
Name: "empty spec",
MountString: "",
MountStrings: "",
ExpectErr: false,
ExpectedMount: Mount{},
ExpectedMount: []Mount{{}},
},
{
Name: "relative container path",
MountString: "/foo/bar:baz/bat:private",
ExpectErr: true,
ExpectedMount: Mount{
HostPath: "/foo/bar",
ContainerPath: "baz/bat",
Propagation: MountPropagationNone,
Name: "relative container path",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add more unit test cases and include some test cases with semi colomon

MountStrings: "/foo/bar:baz/bat:private",
ExpectErr: true,
ExpectedMount: []Mount{
{
HostPath: "/foo/bar",
ContainerPath: "baz/bat",
Propagation: MountPropagationNone,
},
},
},
{
Name: "multi mount string",
MountStrings: "/foo:/foo:ro;/bar:/bar:Z,rshared",
ExpectErr: false,
ExpectedMount: []Mount{
{
HostPath: "/foo",
ContainerPath: "/foo",
Readonly: true,
},
{
HostPath: "/bar",
ContainerPath: "/bar",
SelinuxRelabel: true,
Propagation: MountPropagationBidirectional,
},
},
},
}

for _, tc := range testCases {
mount, err := ParseMountString(tc.MountString)
if err != nil && !tc.ExpectErr {
t.Errorf("Unexpected error for \"%s\": %v", tc.Name, err)
mountStrings := strings.Split(tc.MountStrings, ";")
if len(mountStrings) != len(tc.ExpectedMount) {
t.Errorf("Unexpected length error for \"%s\":\n expected %d\ngot %d", tc.Name, len(tc.ExpectedMount), len(mountStrings))
}
if err == nil && tc.ExpectErr {
t.Errorf("Expected error for \"%s\" but didn't get any: %v %v", tc.Name, mount, err)
}
if mount != tc.ExpectedMount {
t.Errorf("Unexpected mount for \"%s\":\n expected %+v\ngot %+v", tc.Name, tc.ExpectedMount, mount)

for id, mountString := range mountStrings {
mount, err := ParseMountString(mountString)
if err != nil && !tc.ExpectErr {
t.Errorf("Unexpected error for \"%s\": %v", tc.Name, err)
}
if err == nil && tc.ExpectErr {
t.Errorf("Expected error for \"%s\" but didn't get any: %v %v", tc.Name, mount, err)
}
if mount != tc.ExpectedMount[id] {
t.Errorf("Unexpected mount for \"%s\":\n expected %+v\ngot %+v", tc.Name, tc.ExpectedMount, mount)
}
}
}
}
2 changes: 1 addition & 1 deletion pkg/minikube/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ type ClusterConfig struct {
ExtraDisks int // currently only implemented for hyperkit and kvm2
CertExpiration time.Duration
Mount bool
MountString string
MountStrings []string
Mount9PVersion string
MountGID string
MountIP string
Expand Down
5 changes: 3 additions & 2 deletions pkg/minikube/node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func configureMounts(wg *sync.WaitGroup, cc config.ClusterConfig) {
return
}

out.Step(style.Mounting, "Creating mount {{.name}} ...", out.V{"name": cc.MountString})
out.Step(style.Mounting, "Creating mount {{.name}} ...", out.V{"name": cc.MountStrings})
path := os.Args[0]
profile := viper.GetString("profile")

Expand All @@ -102,7 +102,8 @@ func generateMountArgs(profile string, cc config.ClusterConfig) []string {
mountDebugVal = 1
}

args := []string{"mount", cc.MountString}
args := []string{"mount"}
args = append(args, cc.MountStrings...)
flags := []struct {
name string
value string
Expand Down
Loading