225 lines
7.4 KiB
Go
225 lines
7.4 KiB
Go
package netconfig
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// captureLogger records the last formatted message so SetLogger can be
|
|
// verified.
|
|
type captureLogger struct {
|
|
printfCalls int
|
|
printlnCalls int
|
|
}
|
|
|
|
func (c *captureLogger) Printf(format string, args ...interface{}) { c.printfCalls++ }
|
|
func (c *captureLogger) Println(args ...interface{}) { c.printlnCalls++ }
|
|
|
|
func TestConfigOptionsDefaults(t *testing.T) {
|
|
o := newConfigOptions()
|
|
if o.testAddress != defaultInternetTestAddress {
|
|
t.Errorf("default testAddress = %q, want %q", o.testAddress, defaultInternetTestAddress)
|
|
}
|
|
if o.skipConnectivityCheck {
|
|
t.Error("skipConnectivityCheck = true, want false by default")
|
|
}
|
|
if o.connectivityTimeout != defaultConnectivityTimeout {
|
|
t.Errorf("default connectivityTimeout = %v, want %v", o.connectivityTimeout, defaultConnectivityTimeout)
|
|
}
|
|
if o.pingCount != defaultPingCount {
|
|
t.Errorf("default pingCount = %d, want %d", o.pingCount, defaultPingCount)
|
|
}
|
|
if o.pingTimeout != defaultPingTimeout {
|
|
t.Errorf("default pingTimeout = %v, want %v", o.pingTimeout, defaultPingTimeout)
|
|
}
|
|
if o.backupRetention != defaultBackupRetention {
|
|
t.Errorf("default backupRetention = %d, want %d", o.backupRetention, defaultBackupRetention)
|
|
}
|
|
if o.allowPrimaryRemoval {
|
|
t.Error("allowPrimaryRemoval = true, want false by default")
|
|
}
|
|
if o.skipPanels {
|
|
t.Error("skipPanels = true, want false by default")
|
|
}
|
|
}
|
|
|
|
func TestWithTestAddress(t *testing.T) {
|
|
o := newConfigOptions(WithTestAddress("http://canary.example/health"))
|
|
if o.testAddress != "http://canary.example/health" {
|
|
t.Errorf("testAddress = %q, want the overridden value", o.testAddress)
|
|
}
|
|
// An empty address must not clobber the default.
|
|
o = newConfigOptions(WithTestAddress(""))
|
|
if o.testAddress != defaultInternetTestAddress {
|
|
t.Errorf("empty WithTestAddress changed default to %q", o.testAddress)
|
|
}
|
|
}
|
|
|
|
func TestWithConnectivityCheck(t *testing.T) {
|
|
if o := newConfigOptions(WithConnectivityCheck(false)); !o.skipConnectivityCheck {
|
|
t.Error("WithConnectivityCheck(false) did not set skipConnectivityCheck")
|
|
}
|
|
if o := newConfigOptions(WithConnectivityCheck(true)); o.skipConnectivityCheck {
|
|
t.Error("WithConnectivityCheck(true) set skipConnectivityCheck")
|
|
}
|
|
}
|
|
|
|
func TestWithSkipConnectivityCheck(t *testing.T) {
|
|
if o := newConfigOptions(WithSkipConnectivityCheck()); !o.skipConnectivityCheck {
|
|
t.Error("WithSkipConnectivityCheck did not set skipConnectivityCheck")
|
|
}
|
|
}
|
|
|
|
func TestSetLogger(t *testing.T) {
|
|
prev := logger
|
|
defer func() { logger = prev }()
|
|
|
|
cap := &captureLogger{}
|
|
SetLogger(cap)
|
|
if logger != cap {
|
|
t.Fatal("SetLogger did not replace the package logger")
|
|
}
|
|
logger.Printf("hi %d", 1)
|
|
logger.Println("hi")
|
|
if cap.printfCalls != 1 || cap.printlnCalls != 1 {
|
|
t.Errorf("logger not used: printf=%d println=%d", cap.printfCalls, cap.printlnCalls)
|
|
}
|
|
|
|
// A nil logger must be ignored, leaving the current logger in place.
|
|
SetLogger(nil)
|
|
if logger != cap {
|
|
t.Error("SetLogger(nil) replaced the logger")
|
|
}
|
|
}
|
|
|
|
func TestWithConnectivityTimeout(t *testing.T) {
|
|
if o := newConfigOptions(WithConnectivityTimeout(5 * time.Second)); o.connectivityTimeout != 5*time.Second {
|
|
t.Errorf("connectivityTimeout = %v, want 5s", o.connectivityTimeout)
|
|
}
|
|
// Non-positive values must not clobber the default.
|
|
if o := newConfigOptions(WithConnectivityTimeout(0)); o.connectivityTimeout != defaultConnectivityTimeout {
|
|
t.Errorf("zero WithConnectivityTimeout changed default to %v", o.connectivityTimeout)
|
|
}
|
|
}
|
|
|
|
func TestWithPingCount(t *testing.T) {
|
|
if o := newConfigOptions(WithPingCount(3)); o.pingCount != 3 {
|
|
t.Errorf("pingCount = %d, want 3", o.pingCount)
|
|
}
|
|
// Non-positive values must not clobber the default.
|
|
if o := newConfigOptions(WithPingCount(0)); o.pingCount != defaultPingCount {
|
|
t.Errorf("zero WithPingCount changed default to %d", o.pingCount)
|
|
}
|
|
}
|
|
|
|
func TestWithPingTimeout(t *testing.T) {
|
|
if o := newConfigOptions(WithPingTimeout(30 * time.Second)); o.pingTimeout != 30*time.Second {
|
|
t.Errorf("pingTimeout = %v, want 30s", o.pingTimeout)
|
|
}
|
|
if o := newConfigOptions(WithPingTimeout(0)); o.pingTimeout != defaultPingTimeout {
|
|
t.Errorf("zero WithPingTimeout changed default to %v", o.pingTimeout)
|
|
}
|
|
}
|
|
|
|
func TestWithBackupRetention(t *testing.T) {
|
|
if o := newConfigOptions(WithBackupRetention(10)); o.backupRetention != 10 {
|
|
t.Errorf("backupRetention = %d, want 10", o.backupRetention)
|
|
}
|
|
// Zero/negative disables pruning (keeps all backups).
|
|
if o := newConfigOptions(WithBackupRetention(0)); o.backupRetention != 0 {
|
|
t.Errorf("backupRetention = %d, want 0", o.backupRetention)
|
|
}
|
|
}
|
|
|
|
func TestWithAllowPrimaryRemoval(t *testing.T) {
|
|
if o := newConfigOptions(WithAllowPrimaryRemoval(true)); !o.allowPrimaryRemoval {
|
|
t.Error("WithAllowPrimaryRemoval(true) did not set allowPrimaryRemoval")
|
|
}
|
|
}
|
|
|
|
func TestWithSkipPanels(t *testing.T) {
|
|
if o := newConfigOptions(WithSkipPanels(true)); !o.skipPanels {
|
|
t.Error("WithSkipPanels(true) did not set skipPanels")
|
|
}
|
|
if o := newConfigOptions(WithSkipPanels(false)); o.skipPanels {
|
|
t.Error("WithSkipPanels(false) set skipPanels")
|
|
}
|
|
}
|
|
|
|
// applyIfaceDNS must invoke every backend even when an earlier one fails.
|
|
func TestApplyIfaceDNS(t *testing.T) {
|
|
failing := &fakeIfaceBackend{err: errors.New("boom")}
|
|
ok := &fakeIfaceBackend{}
|
|
backends := []namedIfaceBackend{
|
|
{name: "failing", backend: failing},
|
|
{name: "ok", backend: ok},
|
|
}
|
|
applyIfaceDNS(context.Background(), backends, "eth0", []net.IP{net.ParseIP("8.8.8.8")}, []string{"example.com"})
|
|
if failing.dnsCalls != 1 || ok.dnsCalls != 1 {
|
|
t.Errorf("dns calls: failing=%d ok=%d, want 1 each", failing.dnsCalls, ok.dnsCalls)
|
|
}
|
|
}
|
|
|
|
func TestIPStrings(t *testing.T) {
|
|
got := ipStrings([]net.IP{net.ParseIP("8.8.8.8"), nil, net.ParseIP("2001:4860:4860::8888")})
|
|
want := []string{"8.8.8.8", "2001:4860:4860::8888"}
|
|
if len(got) != len(want) {
|
|
t.Fatalf("ipStrings = %v, want %v", got, want)
|
|
}
|
|
for i := range want {
|
|
if got[i] != want[i] {
|
|
t.Errorf("ipStrings[%d] = %q, want %q", i, got[i], want[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestNetplanDNS round-trips DNS through the netplan backend: SetIfaceDNS writes
|
|
// the nameservers block and GetInterfaces parses it back.
|
|
func TestNetplanDNS(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
config := "network:\n version: 2\n ethernets:\n eth0:\n addresses: [192.0.2.10/24]\n"
|
|
if err := os.WriteFile(filepath.Join(tmpDir, "01-test.yaml"), []byte(config), 0644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
np, err := readNetplanConfigDirectory(tmpDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
servers := []net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("2001:4860:4860::8888")}
|
|
if err := np.SetIfaceDNS(context.Background(), "eth0", servers, []string{"example.com", "corp.example"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Re-read from disk so we exercise the written config, not in-memory state.
|
|
np, err = readNetplanConfigDirectory(tmpDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ifaces, err := np.GetInterfaces()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var eth0 *Interface
|
|
for _, i := range ifaces {
|
|
if i.Name == "eth0" {
|
|
eth0 = i
|
|
}
|
|
}
|
|
if eth0 == nil {
|
|
t.Fatal("eth0 not found after SetIfaceDNS")
|
|
}
|
|
if len(eth0.DNS) != 2 || !eth0.DNS[0].Equal(servers[0]) || !eth0.DNS[1].Equal(servers[1]) {
|
|
t.Errorf("DNS = %v, want %v", eth0.DNS, servers)
|
|
}
|
|
if len(eth0.SearchDomains) != 2 || eth0.SearchDomains[0] != "example.com" || eth0.SearchDomains[1] != "corp.example" {
|
|
t.Errorf("SearchDomains = %v, want [example.com corp.example]", eth0.SearchDomains)
|
|
}
|
|
}
|