mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-12 01:58:46 +02:00
202 lines
7.1 KiB
Go
202 lines
7.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"maps"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// writeSysfsInterface builds one interface entry under a fake sysfs root.
|
|
func writeSysfsInterface(t *testing.T, root, name string, files map[string]string, wireless bool) {
|
|
t.Helper()
|
|
dir := filepath.Join(root, name)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatalf("create %s: %v", dir, err)
|
|
}
|
|
for file, content := range files {
|
|
if err := os.WriteFile(filepath.Join(dir, file), []byte(content), 0o644); err != nil {
|
|
t.Fatalf("write %s: %v", file, err)
|
|
}
|
|
}
|
|
if !wireless {
|
|
return
|
|
}
|
|
if err := os.MkdirAll(filepath.Join(dir, "wireless"), 0o755); err != nil {
|
|
t.Fatalf("create the wireless directory: %v", err)
|
|
}
|
|
}
|
|
|
|
// writeSysfsDriverLink points the device entry of an interface at a driver.
|
|
func writeSysfsDriverLink(t *testing.T, root, name, driver string) {
|
|
t.Helper()
|
|
device := filepath.Join(root, name, "device")
|
|
if err := os.MkdirAll(device, 0o755); err != nil {
|
|
t.Fatalf("create %s: %v", device, err)
|
|
}
|
|
if err := os.Symlink(filepath.Join(root, "drivers", driver), filepath.Join(device, "driver")); err != nil {
|
|
t.Fatalf("link the driver of %s: %v", name, err)
|
|
}
|
|
}
|
|
|
|
// writeSysfsDriverUevent names the driver in the uevent of the device.
|
|
func writeSysfsDriverUevent(t *testing.T, root, name, driver string) {
|
|
t.Helper()
|
|
device := filepath.Join(root, name, "device")
|
|
if err := os.MkdirAll(device, 0o755); err != nil {
|
|
t.Fatalf("create %s: %v", device, err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(device, "uevent"), []byte("DRIVER="+driver+"\n"), 0o644); err != nil {
|
|
t.Fatalf("write the uevent of %s: %v", name, err)
|
|
}
|
|
}
|
|
|
|
// stubSysfsNetRoot points the sysfs reader at a temporary directory.
|
|
func stubSysfsNetRoot(t *testing.T) string {
|
|
t.Helper()
|
|
before := sysfsNetRoot
|
|
t.Cleanup(func() { sysfsNetRoot = before })
|
|
sysfsNetRoot = t.TempDir()
|
|
return sysfsNetRoot
|
|
}
|
|
|
|
func Test_sysfsInterfaceInfo(t *testing.T) {
|
|
root := stubSysfsNetRoot(t)
|
|
writeSysfsInterface(t, root, "wlan0", map[string]string{
|
|
"type": "1\n",
|
|
"uevent": "INTERFACE=wlan0\nDEVTYPE=wlan\nIFINDEX=3\n",
|
|
}, true)
|
|
writeSysfsInterface(t, root, "eth0", map[string]string{
|
|
"type": "1\n",
|
|
"uevent": "INTERFACE=eth0\nIFINDEX=2\n",
|
|
}, false)
|
|
writeSysfsInterface(t, root, "tun0", map[string]string{
|
|
"type": "65534\n",
|
|
"uevent": "DEVTYPE=tun\n",
|
|
}, false)
|
|
|
|
tests := []struct {
|
|
name string
|
|
want sysfsInfo
|
|
}{
|
|
{"wlan0", sysfsInfo{Type: 1, DevType: "wlan", Wireless: true}},
|
|
{"eth0", sysfsInfo{Type: 1}},
|
|
{"tun0", sysfsInfo{Type: 65534, DevType: "tun"}},
|
|
{"absent0", sysfsInfo{}},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := sysfsInterfaceInfo(tc.name); got != tc.want {
|
|
t.Errorf("sysfsInterfaceInfo(%q) = %+v, want %+v", tc.name, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_sysfsClass(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
iface string
|
|
info sysfsInfo
|
|
want string
|
|
}{
|
|
{"wireless", "wlan0", sysfsInfo{Wireless: true}, "hardware"},
|
|
{"ethernet", "eth0", sysfsInfo{Type: 1}, "hardware"},
|
|
{"tun devtype", "tun0", sysfsInfo{Type: 65534, DevType: "tun"}, "tunnel"},
|
|
{"tap devtype", "tap0", sysfsInfo{Type: 1, DevType: "tap"}, "tunnel"},
|
|
{"unknown", "sit0", sysfsInfo{Type: 65534}, ""},
|
|
{"docker bridge", "docker0", sysfsInfo{Type: 1, DevType: "bridge"}, "virtual"},
|
|
{"container link", "veth1a2b3c", sysfsInfo{Type: 1}, "virtual"},
|
|
{"libvirt bridge", "virbr0", sysfsInfo{Type: 1, DevType: "bridge"}, "virtual"},
|
|
{"compose bridge", "br-1a2b3c", sysfsInfo{Type: 1, DevType: "bridge"}, "virtual"},
|
|
{"uplink bridge", "br0", sysfsInfo{Type: 1, DevType: "bridge"}, "hardware"},
|
|
{"bond", "bond0", sysfsInfo{Type: 1, DevType: "bond"}, "hardware"},
|
|
{"vlan uplink", "eth0.100", sysfsInfo{Type: 1, DevType: "vlan"}, "hardware"},
|
|
{"removed container link", "veth9f8e7d", sysfsInfo{}, "virtual"},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := sysfsClass(tc.iface, tc.info); got != tc.want {
|
|
t.Errorf("sysfsClass(%q, %+v) = %q, want %q", tc.iface, tc.info, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_platformVirtualInterfaces_namesTheContainerInterfaces(t *testing.T) {
|
|
root := t.TempDir()
|
|
before := sysfsVirtualNetRoot
|
|
t.Cleanup(func() { sysfsVirtualNetRoot = before })
|
|
sysfsVirtualNetRoot = root
|
|
for _, name := range []string{"lo", "docker0", "veth1a2b3c", "virbr0", "br-1a2b3c", "br0", "bond0"} {
|
|
if err := os.MkdirAll(filepath.Join(root, name), 0o755); err != nil {
|
|
t.Fatalf("create %s: %v", name, err)
|
|
}
|
|
}
|
|
|
|
want := map[string]struct{}{"docker0": {}, "veth1a2b3c": {}, "virbr0": {}, "br-1a2b3c": {}}
|
|
if got := platformVirtualInterfaces(); !maps.Equal(got, want) {
|
|
t.Errorf("platformVirtualInterfaces = %v, want %v", got, want)
|
|
}
|
|
if got := sysfsVirtualInterfaces(); len(got) != 7 {
|
|
t.Errorf("virtualInterfaces = %v, want every entry of the kernel directory", got)
|
|
}
|
|
}
|
|
|
|
func Test_platformInterfaceMeta(t *testing.T) {
|
|
root := stubSysfsNetRoot(t)
|
|
writeSysfsInterface(t, root, "ctrld-test-eth0", map[string]string{
|
|
"type": "1\n",
|
|
"uevent": "DEVTYPE=bridge\n",
|
|
}, false)
|
|
|
|
class, hardwarePort, service := platformInterfaceMeta("ctrld-test-eth0")
|
|
if class != "hardware" {
|
|
t.Errorf("class = %q, want hardware", class)
|
|
}
|
|
if hardwarePort != "bridge" {
|
|
t.Errorf("hardware port = %q, want bridge", hardwarePort)
|
|
}
|
|
if service != "" {
|
|
t.Errorf("service = %q, want empty", service)
|
|
}
|
|
}
|
|
|
|
func Test_platformInterfaceMeta_namesTheLink(t *testing.T) {
|
|
root := stubSysfsNetRoot(t)
|
|
writeSysfsInterface(t, root, "wlan0", map[string]string{"type": "1\n", "uevent": "DEVTYPE=wlan\n"}, true)
|
|
writeSysfsInterface(t, root, "eth0", map[string]string{"type": "1\n", "uevent": "INTERFACE=eth0\n"}, false)
|
|
writeSysfsInterface(t, root, "br0", map[string]string{"type": "1\n", "uevent": "DEVTYPE=bridge\n"}, false)
|
|
writeSysfsInterface(t, root, "tun0", map[string]string{"type": "65534\n", "uevent": "DEVTYPE=tun\n"}, false)
|
|
writeSysfsInterface(t, root, "usb0", map[string]string{"type": "1\n"}, false)
|
|
writeSysfsDriverLink(t, root, "usb0", "ipheth")
|
|
writeSysfsInterface(t, root, "usb1", map[string]string{"type": "1\n"}, false)
|
|
writeSysfsDriverUevent(t, root, "usb1", "rndis_host")
|
|
writeSysfsInterface(t, root, "eth1", map[string]string{"type": "1\n"}, false)
|
|
writeSysfsDriverUevent(t, root, "eth1", "e1000e")
|
|
|
|
tests := []struct {
|
|
iface, class, hardwarePort, linkType string
|
|
}{
|
|
{"wlan0", "hardware", "Wi-Fi", "wifi"},
|
|
{"eth0", "hardware", "Ethernet", "ethernet"},
|
|
{"eth1", "hardware", "Ethernet", "ethernet"},
|
|
{"br0", "hardware", "bridge", "unknown"},
|
|
{"tun0", "tunnel", "tun", "tunnel"},
|
|
{"usb0", "hardware", hardwarePortIPhoneUSB, "usb_tether"},
|
|
{"usb1", "hardware", hardwarePortIPhoneUSB, "usb_tether"},
|
|
}
|
|
for _, tc := range tests {
|
|
t.Run(tc.iface, func(t *testing.T) {
|
|
class, hardwarePort, service := platformInterfaceMeta(tc.iface)
|
|
if class != tc.class || hardwarePort != tc.hardwarePort || service != "" {
|
|
t.Errorf("platformInterfaceMeta(%q) = %q, %q, %q, want %q, %q, empty",
|
|
tc.iface, class, hardwarePort, service, tc.class, tc.hardwarePort)
|
|
}
|
|
if got := linkTypeFor(hardwarePort, class); got != tc.linkType {
|
|
t.Errorf("linkTypeFor(%q, %q) = %q, want %q", hardwarePort, class, got, tc.linkType)
|
|
}
|
|
})
|
|
}
|
|
}
|