Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement socket_vmnet network (QEMU) #14989

Merged
merged 10 commits into from
Oct 3, 2022
Merged
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
5 changes: 3 additions & 2 deletions cmd/minikube/cmd/docker-env.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import (
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/shell"
"k8s.io/minikube/pkg/minikube/sysinit"
pkgnetwork "k8s.io/minikube/pkg/network"
kconst "k8s.io/minikube/third_party/kubeadm/app/constants"
)

Expand Down Expand Up @@ -296,12 +297,12 @@ docker-cli install instructions: https://minikube.sigs.k8s.io/docs/tutorials/doc

d := co.CP.Host.Driver
port := constants.DockerDaemonPort
if driver.NeedsPortForward(driverName) && driver.IsKIC(driverName) {
if driver.NeedsPortForward(driverName) {
port, err = oci.ForwardedPort(driverName, cname, port)
if err != nil {
exit.Message(reason.DrvPortForward, "Error getting port binding for '{{.driver_name}} driver: {{.error}}", out.V{"driver_name": driverName, "error": err})
}
} else if driver.NeedsPortForward(driverName) && driverName == driver.QEMU2 {
} else if driver.IsQEMU(driverName) && pkgnetwork.IsUser(co.Config.Network) {
port = d.(*qemu.Driver).EnginePort
}

Expand Down
17 changes: 8 additions & 9 deletions cmd/minikube/cmd/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import (
"k8s.io/minikube/pkg/minikube/service"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/tunnel/kic"
pkgnetwork "k8s.io/minikube/pkg/network"
)

const defaultServiceFormatTemplate = "http://{{.IP}}:{{.Port}}"
Expand Down Expand Up @@ -86,9 +87,12 @@ var serviceCmd = &cobra.Command{
cname := ClusterFlagValue()
co := mustload.Healthy(cname)

// Bail cleanly for qemu2 until implemented
if driver.IsQEMU(co.Config.Driver) {
exit.Message(reason.Unimplemented, "minikube service is not currently implemented with the qemu2 driver. See https://github.com/kubernetes/minikube/issues/14146 for details.")
if driver.IsQEMU(co.Config.Driver) && pkgnetwork.IsUser(co.Config.Network) {
msg := "minikube service is not currently implemented with the user network on QEMU"
if runtime.GOOS == "darwin" {
msg += ", try starting minikube with '--network=socket_vmnet'"
}
exit.Message(reason.Unimplemented, msg)
}

var services service.URLs
Expand Down Expand Up @@ -146,10 +150,8 @@ You may select another namespace by using 'minikube service {{.service}} -n <nam
}
}

if driver.NeedsPortForward(co.Config.Driver) && driver.IsKIC(co.Config.Driver) && services != nil {
if driver.NeedsPortForward(co.Config.Driver) && services != nil {
startKicServiceTunnel(services, cname, co.Config.Driver)
} else if driver.NeedsPortForward(co.Config.Driver) && driver.IsQEMU(co.Config.Driver) && services != nil {
startQemuServiceTunnel(services, cname, co.Config.Driver)
} else if !serviceURLMode {
openURLs(data)
}
Expand Down Expand Up @@ -222,9 +224,6 @@ func startKicServiceTunnel(services service.URLs, configName, driverName string)
<-ctrlC
}

func startQemuServiceTunnel(services service.URLs, configName, driverName string) {
}

func mutateURLs(serviceName string, urls []string) ([]string, error) {
formattedUrls := make([]string, 0)
for _, rawURL := range urls {
Expand Down
24 changes: 21 additions & 3 deletions cmd/minikube/cmd/start_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cmd

import (
"fmt"
"runtime"
"strings"
"time"

Expand Down Expand Up @@ -457,6 +458,23 @@ func getCNIConfig(cmd *cobra.Command) string {
return chosenCNI
}

func getNetwork(driverName string) string {
n := viper.GetString(network)
if !driver.IsQEMU(driverName) {
return n
}
if n == "" {
if runtime.GOOS == "darwin" {
out.WarningT("The default network for QEMU will change from 'user' to 'socket_vmnet' in a future release")
}
n = "user"
}
if n == "user" && runtime.GOOS == "darwin" {
out.WarningT("You are using the QEMU driver without a dedicated network, which doesn't support `minikube service` & `minikube tunnel` commands.\nTo try the experimental dedicated network see: https://minikube.sigs.k8s.io/docs/drivers/qemu/#networking")
}
return n
spowelljr marked this conversation as resolved.
Show resolved Hide resolved
}

// 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 All @@ -471,8 +489,8 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
out.WarningT("--network flag is only valid with the docker/podman, KVM and Qemu drivers, it will be ignored")
}

if driver.IsQEMU(drvName) && viper.GetString(network) == "socket" {
out.WarningT("Using qemu with --network=socket for 'socket_vmnet' is experimental")
if driver.IsQEMU(drvName) && viper.GetString(network) == "socket_vmnet" {
out.WarningT("Using qemu with 'socket_vmnet' network is experimental")
}

checkNumaCount(k8sVersion)
Expand All @@ -485,7 +503,7 @@ func generateNewConfigFromFlags(cmd *cobra.Command, k8sVersion string, rtime str
EmbedCerts: viper.GetBool(embedCerts),
MinikubeISO: viper.GetString(isoURL),
KicBaseImage: viper.GetString(kicBaseImage),
Network: viper.GetString(network),
Network: getNetwork(drvName),
Subnet: viper.GetString(subnet),
Memory: getMemorySize(cmd, drvName),
CPUs: getCPUCount(drvName),
Expand Down
23 changes: 9 additions & 14 deletions cmd/minikube/cmd/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"os"
"os/signal"
"path/filepath"
"runtime"
"strconv"

"github.com/spf13/cobra"
Expand All @@ -38,6 +39,7 @@ import (
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/tunnel"
"k8s.io/minikube/pkg/minikube/tunnel/kic"
pkgnetwork "k8s.io/minikube/pkg/network"
)

var cleanup bool
Expand All @@ -56,9 +58,12 @@ var tunnelCmd = &cobra.Command{
cname := ClusterFlagValue()
co := mustload.Healthy(cname)

// Bail cleanly for qemu2 until implemented
if driver.IsQEMU(co.Config.Driver) {
exit.Message(reason.Unimplemented, "minikube tunnel is not currently implemented with the qemu2 driver. See https://github.com/kubernetes/minikube/issues/14146 for details.")
if driver.IsQEMU(co.Config.Driver) && pkgnetwork.IsUser(co.Config.Network) {
msg := "minikube tunnel is not currently implemented with the user network on QEMU"
if runtime.GOOS == "darwin" {
msg += ", try starting minikube with '--network=socket_vmnet'"
}
exit.Message(reason.Unimplemented, msg)
}

if cleanup {
Expand All @@ -85,7 +90,7 @@ var tunnelCmd = &cobra.Command{
cancel()
}()

if useSSHTunnel(co.Config.Driver) {
if driver.NeedsPortForward(co.Config.Driver) || bindAddress != "" {
port, err := oci.ForwardedPort(co.Config.Driver, cname, 22)
if err != nil {
exit.Error(reason.DrvPortForward, "error getting ssh port", err)
Expand All @@ -111,16 +116,6 @@ var tunnelCmd = &cobra.Command{
},
}

func useSSHTunnel(driverName string) bool {
if !driver.IsKIC(driverName) {
return false
}
if driver.NeedsPortForward(driverName) {
return true
}
return bindAddress != ""
}

func outputTunnelStarted() {
out.Styled(style.Success, "Tunnel successfully started")
out.Ln("")
Expand Down
2 changes: 1 addition & 1 deletion pkg/addons/addons.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func addonSpecificChecks(cc *config.ClusterConfig, name string, enable bool, run
}

if name == "registry" {
if driver.NeedsPortForward(cc.Driver) && driver.IsKIC(cc.Driver) {
if driver.NeedsPortForward(cc.Driver) {
port, err := oci.ForwardedPort(cc.Driver, cc.Name, constants.RegistryAddonPort)
if err != nil {
return false, errors.Wrap(err, "registry port")
Expand Down
2 changes: 1 addition & 1 deletion pkg/addons/addons_autopause.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func enableOrDisableAutoPause(cc *config.ClusterConfig, name, val string) error
port := co.CP.Port // API server port
if enable { // if enable, calculate the forwarded port
port = constants.AutoPauseProxyPort
if driver.NeedsPortForward(cc.Driver) && driver.IsKIC(cc.Driver) {
if driver.NeedsPortForward(cc.Driver) {
port, err = oci.ForwardedPort(cc.Driver, cc.Name, port)
if err != nil {
klog.ErrorS(err, "failed to get forwarded port for", "auto-pause port", port)
Expand Down
92 changes: 92 additions & 0 deletions pkg/drivers/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ limitations under the License.
package drivers

import (
"bufio"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"strings"
"syscall"

"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnflag"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/ssh"
Expand All @@ -33,6 +37,11 @@ import (
"k8s.io/minikube/pkg/util"
)

// LeasesPath is the path to dhcpd leases
const LeasesPath = "/var/db/dhcpd_leases"

var leadingZeroRegexp = regexp.MustCompile(`0([A-Fa-f0-9](:|$))`)

// This file is for common code shared among internal machine drivers
// Code here should not be called from within minikube

Expand Down Expand Up @@ -147,3 +156,86 @@ func fixMachinePermissions(path string) error {
}
return nil
}

// DHCPEntry holds a parsed DNS entry
type DHCPEntry struct {
Name string
IPAddress string
HWAddress string
ID string
Lease string
}

// GetIPAddressByMACAddress gets the IP address of a MAC address
func GetIPAddressByMACAddress(mac string) (string, error) {
return getIPAddressFromFile(mac, LeasesPath)
}

func getIPAddressFromFile(mac, path string) (string, error) {
log.Debugf("Searching for %s in %s ...", mac, path)
file, err := os.Open(path)
if err != nil {
return "", err
}
defer file.Close()

dhcpEntries, err := parseDHCPdLeasesFile(file)
if err != nil {
return "", err
}
log.Debugf("Found %d entries in %s!", len(dhcpEntries), path)
for _, dhcpEntry := range dhcpEntries {
log.Debugf("dhcp entry: %+v", dhcpEntry)
if dhcpEntry.HWAddress == mac {
log.Debugf("Found match: %s", mac)
return dhcpEntry.IPAddress, nil
}
}
return "", fmt.Errorf("could not find an IP address for %s", mac)
}

func parseDHCPdLeasesFile(file io.Reader) ([]DHCPEntry, error) {
var (
dhcpEntry *DHCPEntry
dhcpEntries []DHCPEntry
)

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "{" {
dhcpEntry = new(DHCPEntry)
continue
} else if line == "}" {
dhcpEntries = append(dhcpEntries, *dhcpEntry)
continue
}

split := strings.SplitN(line, "=", 2)
if len(split) != 2 {
return nil, fmt.Errorf("invalid line in dhcp leases file: %s", line)
}
key, val := split[0], split[1]
switch key {
case "name":
dhcpEntry.Name = val
case "ip_address":
dhcpEntry.IPAddress = val
case "hw_address":
// The mac addresses have a '1,' at the start.
dhcpEntry.HWAddress = val[2:]
case "identifier":
dhcpEntry.ID = val
case "lease":
dhcpEntry.Lease = val
default:
return dhcpEntries, fmt.Errorf("unable to parse line: %s", line)
}
}
return dhcpEntries, scanner.Err()
}

// TrimMacAddress trimming "0" of the ten's digit
func TrimMacAddress(rawUUID string) string {
return leadingZeroRegexp.ReplaceAllString(rawUUID, "$1")
}
78 changes: 78 additions & 0 deletions pkg/drivers/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,81 @@ func Test_createDiskImage(t *testing.T) {
t.Errorf("Disk size is %v, want %v", fi.Size(), sizeInBytes)
}
}

var validLeases = []byte(`{
name=foo
ip_address=1.2.3.4
hw_address=1,a1:b2:c3:d4:e5:f6
identifier=1,a2:b3:c4:d5:e6:f7
lease=0x597e1267
}
{
name=bar
ip_address=192.168.64.3
hw_address=1,a4:b5:c6:d7:e8:f9
identifier=1,a0:b0:c0:d0:e0:f0
lease=0x597e1267
}
{
name=bar
ip_address=192.168.64.4
hw_address=1,a5:b6:c7:d8:e9:f1
identifier=1,a5:b6:c7:d8:e9:f1
lease=0x597e1268
}`)

func Test_getIpAddressFromFile(t *testing.T) {
tmpdir := tests.MakeTempDir(t)

dhcpFile := filepath.Join(tmpdir, "dhcp")
if err := os.WriteFile(dhcpFile, validLeases, 0644); err != nil {
t.Fatalf("writefile: %v", err)
}

invalidFile := filepath.Join(tmpdir, "invalid")
if err := os.WriteFile(invalidFile, []byte("foo"), 0644); err != nil {
t.Fatalf("writefile: %v", err)
}

type args struct {
mac string
path string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
"valid",
args{"a1:b2:c3:d4:e5:f6", dhcpFile},
"1.2.3.4",
false,
},
{
"duplicate",
args{"a4:b5:c6:d7:e8:f9", dhcpFile},
"192.168.64.3",
false,
},
{
"invalid",
args{"a1:b2:c3:d4:e5:f6", invalidFile},
"",
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := getIPAddressFromFile(tt.args.mac, tt.args.path)
if (err != nil) != tt.wantErr {
t.Errorf("getIPAddressFromFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("getIPAddressFromFile() = %v, want %v", got, tt.want)
}
})
}
}
Loading