Skip to content

Commit

Permalink
Merge pull request #14989 from spowelljr/socketVMNet
Browse files Browse the repository at this point in the history
Implement socket_vmnet network (QEMU)
  • Loading branch information
spowelljr authored Oct 3, 2022
2 parents 63b9bb0 + 463db44 commit e961a52
Show file tree
Hide file tree
Showing 28 changed files with 415 additions and 329 deletions.
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
}

// 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

0 comments on commit e961a52

Please sign in to comment.