diff --git a/cmd/minikube/cmd/start_flags.go b/cmd/minikube/cmd/start_flags.go index f7b51c4ba05f..1988677eeb42 100644 --- a/cmd/minikube/cmd/start_flags.go +++ b/cmd/minikube/cmd/start_flags.go @@ -107,6 +107,7 @@ const ( forceSystemd = "force-systemd" kicBaseImage = "base-image" ports = "ports" + network = "network" startNamespace = "namespace" trace = "trace" ) @@ -152,6 +153,7 @@ func initMinikubeFlags() { startCmd.Flags().Bool(preload, true, "If set, download tarball of preloaded images if available to improve start time. Defaults to true.") startCmd.Flags().Bool(deleteOnFailure, false, "If set, delete the current cluster if start fails and try again. Defaults to false.") startCmd.Flags().Bool(forceSystemd, false, "If set, force the container runtime to use sytemd as cgroup manager. Currently available for docker and crio. Defaults to false.") + startCmd.Flags().StringP(network, "", "", "network to run minikube with. Only available with the docker/podman drivers. If left empty, minikube will create a new network.") startCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "Format to print stdout in. Options include: [text,json]") startCmd.Flags().StringP(trace, "", "", "Send trace events. Options include: [gcp]") } @@ -293,12 +295,17 @@ func generateClusterConfig(cmd *cobra.Command, existing *config.ClusterConfig, k out.WarningT("With --network-plugin=cni, you will need to provide your own CNI. See --cni flag as a user-friendly alternative") } + if !driver.IsKIC(drvName) && viper.GetString(network) != "" { + out.WarningT("--network flag is only valid with the docker/podman drivers, it will be ignored") + } + cc = config.ClusterConfig{ Name: ClusterFlagValue(), KeepContext: viper.GetBool(keepContext), EmbedCerts: viper.GetBool(embedCerts), MinikubeISO: viper.GetString(isoURL), KicBaseImage: viper.GetString(kicBaseImage), + Network: viper.GetString(network), Memory: mem, CPUs: viper.GetInt(cpus), DiskSize: diskSize, diff --git a/pkg/drivers/kic/kic.go b/pkg/drivers/kic/kic.go index a653ca36e6a7..9cbc9152b5c7 100644 --- a/pkg/drivers/kic/kic.go +++ b/pkg/drivers/kic/kic.go @@ -86,10 +86,14 @@ func (d *Driver) Create() error { APIServerPort: d.NodeConfig.APIServerPort, } - if gateway, err := oci.CreateNetwork(d.OCIBinary, d.NodeConfig.ClusterName); err != nil { + networkName := d.NodeConfig.Network + if networkName == "" { + networkName = d.NodeConfig.ClusterName + } + if gateway, err := oci.CreateNetwork(d.OCIBinary, networkName); err != nil { out.WarningT("Unable to create dedicated network, this might result in cluster IP change after restart: {{.error}}", out.V{"error": err}) - } else { - params.Network = d.NodeConfig.ClusterName + } else if gateway != nil { + params.Network = networkName ip := gateway.To4() // calculate the container IP based on guessing the machine index ip[3] += byte(driver.IndexFromMachineName(d.NodeConfig.MachineName)) diff --git a/pkg/drivers/kic/oci/network_create.go b/pkg/drivers/kic/oci/network_create.go index 5a49fc77e646..3f145e2e84e1 100644 --- a/pkg/drivers/kic/oci/network_create.go +++ b/pkg/drivers/kic/oci/network_create.go @@ -44,7 +44,7 @@ const dockerDefaultBridge = "bridge" const podmanDefaultBridge = "podman" // CreateNetwork creates a network returns gateway and error, minikube creates one network per cluster -func CreateNetwork(ociBin string, clusterName string) (net.IP, error) { +func CreateNetwork(ociBin string, networkName string) (net.IP, error) { var defaultBridgeName string if ociBin == Docker { defaultBridgeName = dockerDefaultBridge @@ -52,9 +52,13 @@ func CreateNetwork(ociBin string, clusterName string) (net.IP, error) { if ociBin == Podman { defaultBridgeName = podmanDefaultBridge } + if networkName == defaultBridgeName { + klog.Infof("skipping creating network since default network %s was specified", networkName) + return nil, nil + } // check if the network already exists - info, err := containerNetworkInspect(ociBin, clusterName) + info, err := containerNetworkInspect(ociBin, networkName) if err == nil { klog.Infof("Found existing network %+v", info) return info.gateway, nil @@ -71,7 +75,7 @@ func CreateNetwork(ociBin string, clusterName string) (net.IP, error) { // Rather than iterate through all of the valid subnets, give up at 20 to avoid a lengthy user delay for something that is unlikely to work. // will be like 192.168.49.0/24 ,...,192.168.239.0/24 for attempts < 20 { - info.gateway, err = tryCreateDockerNetwork(ociBin, subnetAddr, defaultSubnetMask, info.mtu, clusterName) + info.gateway, err = tryCreateDockerNetwork(ociBin, subnetAddr, defaultSubnetMask, info.mtu, networkName) if err == nil { return info.gateway, nil } diff --git a/pkg/drivers/kic/types.go b/pkg/drivers/kic/types.go index 32f314ba4a6a..e060fec10b1c 100644 --- a/pkg/drivers/kic/types.go +++ b/pkg/drivers/kic/types.go @@ -61,5 +61,6 @@ type Config struct { Envs map[string]string // key,value of environment variables passed to the node KubernetesVersion string // Kubernetes version to install ContainerRuntime string // container runtime kic is running + Network string // network to run with kic ExtraArgs []string // a list of any extra option to pass to oci binary during creation time, for example --expose 8080... } diff --git a/pkg/minikube/config/types.go b/pkg/minikube/config/types.go index 8dea98496762..00cc1b54d4d3 100644 --- a/pkg/minikube/config/types.go +++ b/pkg/minikube/config/types.go @@ -73,6 +73,7 @@ type ClusterConfig struct { StartHostTimeout time.Duration ScheduledStop *ScheduledStopConfig ExposedPorts []string // Only used by the docker and podman driver + Network string // only used by docker driver MultiNodeRequested bool } diff --git a/pkg/minikube/registry/drvs/docker/docker.go b/pkg/minikube/registry/drvs/docker/docker.go index df2771e80449..4a16ded7469d 100644 --- a/pkg/minikube/registry/drvs/docker/docker.go +++ b/pkg/minikube/registry/drvs/docker/docker.go @@ -79,6 +79,7 @@ func configure(cc config.ClusterConfig, n config.Node) (interface{}, error) { KubernetesVersion: cc.KubernetesConfig.KubernetesVersion, ContainerRuntime: cc.KubernetesConfig.ContainerRuntime, ExtraArgs: extraArgs, + Network: cc.Network, }), nil } diff --git a/site/content/en/docs/commands/start.md b/site/content/en/docs/commands/start.md index 3d12e8dd1f64..edaea7cb3a19 100644 --- a/site/content/en/docs/commands/start.md +++ b/site/content/en/docs/commands/start.md @@ -77,6 +77,7 @@ minikube start [flags] --namespace string The named space to activate after start (default "default") --nat-nic-type string NIC Type used for nat network. One of Am79C970A, Am79C973, 82540EM, 82543GC, 82545EM, or virtio (virtualbox driver only) (default "virtio") --native-ssh Use native Golang SSH client (default true). Set to 'false' to use the command line 'ssh' command when accessing the docker machine. Useful for the machine drivers when they will not start with 'Waiting for SSH'. (default true) + --network string network to run minikube with. Only available with the docker/podman drivers. If left empty, minikube will create a new network. --network-plugin string Kubelet network plug-in to use (default: auto) --nfs-share strings Local folders to share with Guest via NFS mounts (hyperkit driver only) --nfs-shares-root string Where to root the NFS Shares, defaults to /nfsshares (hyperkit driver only) (default "/nfsshares") diff --git a/test/integration/kic_custom_network_test.go b/test/integration/kic_custom_network_test.go new file mode 100644 index 000000000000..d3b43c4d3edb --- /dev/null +++ b/test/integration/kic_custom_network_test.go @@ -0,0 +1,106 @@ +// +build integration + +/* +Copyright 2020 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package integration + +import ( + "context" + "fmt" + "os/exec" + "strings" + "testing" + + "k8s.io/minikube/pkg/drivers/kic/oci" +) + +func TestKicCustomNetwork(t *testing.T) { + if !KicDriver() { + t.Skip("only runs with docker driver") + } + + tests := []struct { + description string + networkName string + }{ + { + description: "create custom network", + }, { + description: "use default bridge network", + networkName: "bridge", + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + profile := UniqueProfileName("docker-network") + ctx, cancel := context.WithTimeout(context.Background(), Minutes(5)) + defer Cleanup(t, profile, cancel) + + startArgs := []string{"start", "-p", profile, fmt.Sprintf("--network=%s", test.networkName)} + c := exec.CommandContext(ctx, Target(), startArgs...) + rr, err := Run(t, c) + if err != nil { + t.Fatalf("%v failed: %v\n%v", rr.Command(), err, rr.Output()) + } + nn := test.networkName + if nn == "" { + nn = profile + } + verifyNetworkExists(ctx, t, nn) + }) + } +} + +func TestKicExistingNetwork(t *testing.T) { + if !KicDriver() { + t.Skip("only runs with docker driver") + } + // create custom network + networkName := "existing-network" + if _, err := oci.CreateNetwork(oci.Docker, networkName); err != nil { + t.Fatalf("error creating network: %v", err) + } + defer func() { + if err := oci.DeleteKICNetworks(oci.Docker); err != nil { + t.Logf("error deleting kic network, may need to delete manually: %v", err) + } + }() + profile := UniqueProfileName("existing-network") + ctx, cancel := context.WithTimeout(context.Background(), Minutes(5)) + defer Cleanup(t, profile, cancel) + + verifyNetworkExists(ctx, t, networkName) + + startArgs := []string{"start", "-p", profile, fmt.Sprintf("--network=%s", networkName)} + c := exec.CommandContext(ctx, Target(), startArgs...) + rr, err := Run(t, c) + if err != nil { + t.Fatalf("%v failed: %v\n%v", rr.Command(), err, rr.Output()) + } +} + +func verifyNetworkExists(ctx context.Context, t *testing.T, networkName string) { + c := exec.CommandContext(ctx, "docker", "network", "ls", "--format", "{{.Name}}") + rr, err := Run(t, c) + if err != nil { + t.Fatalf("%v failed: %v\n%v", rr.Command(), err, rr.Output()) + } + if output := rr.Output(); !strings.Contains(output, networkName) { + t.Fatalf("%s network is not listed by [%v]: %v", networkName, c.Args, output) + } +}