mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-10-12 01:58:46 +02:00
feat(cli): put the network state of the host in the log journal
The journal records the network state of the host and each change of that state. The support team reads one file to see the interfaces, the routes, the resolvers, and the recoveries around an incident. The same file shows the health of the query path. A change that touches only AirDrop or virtual interfaces runs no DNS work.
This commit is contained in:
1 parent
8934e09428
commit
8ae09ac98e
68 files changed
+10458
-442
No files matched your search
@@ -0,0 +1,201 @@
|
||||
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 := virtualInterfaces(); 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user