181 lines
6 KiB
Go
181 lines
6 KiB
Go
package netconfig
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// 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()
|
|
assert.Equal(t, defaultInternetTestAddress, o.testAddress)
|
|
assert.False(t, o.skipConnectivityCheck)
|
|
assert.Equal(t, defaultConnectivityTimeout, o.connectivityTimeout)
|
|
assert.Equal(t, defaultPingCount, o.pingCount)
|
|
assert.Equal(t, defaultPingTimeout, o.pingTimeout)
|
|
assert.Equal(t, defaultBackupRetention, o.backupRetention)
|
|
assert.Equal(t, defaultServiceReadyTimeout, o.serviceReadyTimeout)
|
|
assert.False(t, o.allowPrimaryRemoval)
|
|
assert.False(t, o.skipPanels)
|
|
assert.False(t, o.allowNoBackends)
|
|
}
|
|
|
|
func TestWithServiceReadyTimeout(t *testing.T) {
|
|
o := newConfigOptions(WithServiceReadyTimeout(90 * time.Second))
|
|
assert.Equal(t, 90*time.Second, o.serviceReadyTimeout)
|
|
|
|
// Zero and negative both mean "do not wait", and must be preserved rather
|
|
// than falling back to the default the way WithTestAddress("") does.
|
|
o = newConfigOptions(WithServiceReadyTimeout(0))
|
|
assert.Equal(t, time.Duration(0), o.serviceReadyTimeout)
|
|
o = newConfigOptions(WithServiceReadyTimeout(-1))
|
|
assert.Equal(t, -1*time.Nanosecond, o.serviceReadyTimeout)
|
|
}
|
|
|
|
func TestWithTestAddress(t *testing.T) {
|
|
o := newConfigOptions(WithTestAddress("http://canary.example/health"))
|
|
assert.Equal(t, "http://canary.example/health", o.testAddress)
|
|
// An empty address must not clobber the default.
|
|
o = newConfigOptions(WithTestAddress(""))
|
|
assert.Equal(t, defaultInternetTestAddress, o.testAddress)
|
|
}
|
|
|
|
func TestWithConnectivityCheck(t *testing.T) {
|
|
o := newConfigOptions(WithConnectivityCheck(false))
|
|
assert.True(t, o.skipConnectivityCheck)
|
|
o = newConfigOptions(WithConnectivityCheck(true))
|
|
assert.False(t, o.skipConnectivityCheck)
|
|
}
|
|
|
|
func TestWithSkipConnectivityCheck(t *testing.T) {
|
|
o := newConfigOptions(WithSkipConnectivityCheck())
|
|
assert.True(t, o.skipConnectivityCheck)
|
|
}
|
|
|
|
func TestSetLogger(t *testing.T) {
|
|
prev := logger
|
|
defer func() { logger = prev }()
|
|
|
|
cap := &captureLogger{}
|
|
SetLogger(cap)
|
|
require.Equal(t, cap, logger)
|
|
logger.Printf("hi %d", 1)
|
|
logger.Println("hi")
|
|
assert.Equal(t, 1, cap.printfCalls)
|
|
assert.Equal(t, 1, cap.printlnCalls)
|
|
|
|
// A nil logger must be ignored, leaving the current logger in place.
|
|
SetLogger(nil)
|
|
assert.Equal(t, cap, logger)
|
|
}
|
|
|
|
func TestWithConnectivityTimeout(t *testing.T) {
|
|
o := newConfigOptions(WithConnectivityTimeout(5 * time.Second))
|
|
assert.Equal(t, 5*time.Second, o.connectivityTimeout)
|
|
// Non-positive values must not clobber the default.
|
|
o = newConfigOptions(WithConnectivityTimeout(0))
|
|
assert.Equal(t, defaultConnectivityTimeout, o.connectivityTimeout)
|
|
}
|
|
|
|
func TestWithPingCount(t *testing.T) {
|
|
o := newConfigOptions(WithPingCount(3))
|
|
assert.Equal(t, 3, o.pingCount)
|
|
// Non-positive values must not clobber the default.
|
|
o = newConfigOptions(WithPingCount(0))
|
|
assert.Equal(t, defaultPingCount, o.pingCount)
|
|
}
|
|
|
|
func TestWithPingTimeout(t *testing.T) {
|
|
o := newConfigOptions(WithPingTimeout(30 * time.Second))
|
|
assert.Equal(t, 30*time.Second, o.pingTimeout)
|
|
o = newConfigOptions(WithPingTimeout(0))
|
|
assert.Equal(t, defaultPingTimeout, o.pingTimeout)
|
|
}
|
|
|
|
func TestWithBackupRetention(t *testing.T) {
|
|
o := newConfigOptions(WithBackupRetention(10))
|
|
assert.Equal(t, 10, o.backupRetention)
|
|
// Zero/negative disables pruning (keeps all backups).
|
|
o = newConfigOptions(WithBackupRetention(0))
|
|
assert.Equal(t, 0, o.backupRetention)
|
|
}
|
|
|
|
func TestWithAllowPrimaryRemoval(t *testing.T) {
|
|
o := newConfigOptions(WithAllowPrimaryRemoval(true))
|
|
assert.True(t, o.allowPrimaryRemoval)
|
|
}
|
|
|
|
func TestWithSkipPanels(t *testing.T) {
|
|
o := newConfigOptions(WithSkipPanels(true))
|
|
assert.True(t, o.skipPanels)
|
|
o = newConfigOptions(WithSkipPanels(false))
|
|
assert.False(t, o.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"})
|
|
assert.Equal(t, 1, failing.dnsCalls)
|
|
assert.Equal(t, 1, 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"}
|
|
require.Equal(t, want, got)
|
|
}
|
|
|
|
// 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"
|
|
err := os.WriteFile(filepath.Join(tmpDir, "01-test.yaml"), []byte(config), 0644)
|
|
require.NoError(t, err)
|
|
|
|
np, err := readNetplanConfigDirectory(tmpDir)
|
|
require.NoError(t, err)
|
|
|
|
servers := []net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("2001:4860:4860::8888")}
|
|
err = np.SetIfaceDNS(context.Background(), "eth0", servers, []string{"example.com", "corp.example"})
|
|
require.NoError(t, err)
|
|
|
|
// Re-read from disk so we exercise the written config, not in-memory state.
|
|
np, err = readNetplanConfigDirectory(tmpDir)
|
|
require.NoError(t, err)
|
|
ifaces, err := np.GetInterfaces()
|
|
require.NoError(t, err)
|
|
|
|
var eth0 *Interface
|
|
for _, i := range ifaces {
|
|
if i.Name == "eth0" {
|
|
eth0 = i
|
|
}
|
|
}
|
|
require.NotNil(t, eth0)
|
|
assert.True(t, len(eth0.DNS) == 2 && eth0.DNS[0].Equal(servers[0]) && eth0.DNS[1].Equal(servers[1]))
|
|
assert.True(t, len(eth0.SearchDomains) == 2 && eth0.SearchDomains[0] == "example.com" && eth0.SearchDomains[1] == "corp.example")
|
|
}
|