559 lines
13 KiB
Go
559 lines
13 KiB
Go
package tools
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestNewTalosctl(t *testing.T) {
|
|
t.Run("creates Talosctl instance without config", func(t *testing.T) {
|
|
tc := NewTalosctl()
|
|
if tc == nil {
|
|
t.Fatal("NewTalosctl() returned nil")
|
|
}
|
|
if tc.talosconfigPath != "" {
|
|
t.Error("talosconfigPath should be empty for NewTalosctl()")
|
|
}
|
|
})
|
|
|
|
t.Run("creates Talosctl instance with config", func(t *testing.T) {
|
|
configPath := "/path/to/talosconfig"
|
|
tc := NewTalosconfigWithConfig(configPath)
|
|
if tc == nil {
|
|
t.Fatal("NewTalosconfigWithConfig() returned nil")
|
|
}
|
|
if tc.talosconfigPath != configPath {
|
|
t.Errorf("talosconfigPath = %q, want %q", tc.talosconfigPath, configPath)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestTalosconfigBuildArgs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
talosconfigPath string
|
|
baseArgs []string
|
|
wantPrefix []string
|
|
}{
|
|
{
|
|
name: "no talosconfig adds no prefix",
|
|
talosconfigPath: "",
|
|
baseArgs: []string{"version", "--short"},
|
|
wantPrefix: nil,
|
|
},
|
|
{
|
|
name: "with talosconfig adds prefix",
|
|
talosconfigPath: "/path/to/talosconfig",
|
|
baseArgs: []string{"version", "--short"},
|
|
wantPrefix: []string{"--talosconfig", "/path/to/talosconfig"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tc := &Talosctl{talosconfigPath: tt.talosconfigPath}
|
|
got := tc.buildArgs(tt.baseArgs)
|
|
|
|
if tt.wantPrefix == nil {
|
|
// Should return baseArgs unchanged
|
|
if len(got) != len(tt.baseArgs) {
|
|
t.Errorf("buildArgs() length = %d, want %d", len(got), len(tt.baseArgs))
|
|
}
|
|
for i, arg := range tt.baseArgs {
|
|
if i >= len(got) || got[i] != arg {
|
|
t.Errorf("buildArgs()[%d] = %q, want %q", i, got[i], arg)
|
|
}
|
|
}
|
|
} else {
|
|
// Should have prefix + baseArgs
|
|
expectedLen := len(tt.wantPrefix) + len(tt.baseArgs)
|
|
if len(got) != expectedLen {
|
|
t.Errorf("buildArgs() length = %d, want %d", len(got), expectedLen)
|
|
}
|
|
// Check prefix
|
|
for i, arg := range tt.wantPrefix {
|
|
if i >= len(got) || got[i] != arg {
|
|
t.Errorf("buildArgs() prefix[%d] = %q, want %q", i, got[i], arg)
|
|
}
|
|
}
|
|
// Check baseArgs follow prefix
|
|
for i, arg := range tt.baseArgs {
|
|
idx := len(tt.wantPrefix) + i
|
|
if idx >= len(got) || got[idx] != arg {
|
|
t.Errorf("buildArgs()[%d] = %q, want %q", idx, got[idx], arg)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGenConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
clusterName string
|
|
endpoint string
|
|
outputDir string
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "gen config with valid params",
|
|
clusterName: "test-cluster",
|
|
endpoint: "https://192.168.1.100:6443",
|
|
outputDir: "testdata",
|
|
skipTest: true, // Skip actual execution
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary")
|
|
}
|
|
|
|
tmpDir := t.TempDir()
|
|
tc := NewTalosctl()
|
|
err := tc.GenConfig(tt.clusterName, tt.endpoint, tmpDir)
|
|
|
|
// This will fail without talosctl, but tests the method signature
|
|
if err == nil {
|
|
// If it somehow succeeds, verify files were created
|
|
expectedFiles := []string{
|
|
"controlplane.yaml",
|
|
"worker.yaml",
|
|
"talosconfig",
|
|
}
|
|
for _, file := range expectedFiles {
|
|
path := filepath.Join(tmpDir, file)
|
|
if _, err := os.Stat(path); os.IsNotExist(err) {
|
|
t.Errorf("Expected file not created: %s", file)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigApplyConfig(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
configFile string
|
|
insecure bool
|
|
talosconfigPath string
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "apply config with all params",
|
|
nodeIP: "192.168.1.100",
|
|
configFile: "/path/to/config.yaml",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
{
|
|
name: "apply config with talosconfig",
|
|
nodeIP: "192.168.1.100",
|
|
configFile: "/path/to/config.yaml",
|
|
insecure: false,
|
|
talosconfigPath: "/path/to/talosconfig",
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
err := tc.ApplyConfig(tt.nodeIP, tt.configFile, tt.insecure, tt.talosconfigPath)
|
|
|
|
// Will fail without talosctl, but tests method signature
|
|
_ = err
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGetDisks(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
insecure bool
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "get disks in insecure mode",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary and running node")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
disks, err := tc.GetDisks(tt.nodeIP, tt.insecure)
|
|
|
|
if err == nil {
|
|
// If successful, verify return type
|
|
if disks == nil {
|
|
t.Error("GetDisks() returned nil slice without error")
|
|
}
|
|
// Each disk should have path and size
|
|
for i, disk := range disks {
|
|
if disk.Path == "" {
|
|
t.Errorf("disk[%d].Path is empty", i)
|
|
}
|
|
if disk.Size <= 0 {
|
|
t.Errorf("disk[%d].Size = %d, want > 0", i, disk.Size)
|
|
}
|
|
// Size should be > 10GB per filtering
|
|
if disk.Size <= 10000000000 {
|
|
t.Errorf("disk[%d].Size = %d, should be filtered (> 10GB)", i, disk.Size)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGetLinks(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
insecure bool
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "get links in insecure mode",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary and running node")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
links, err := tc.GetLinks(tt.nodeIP, tt.insecure)
|
|
|
|
if err == nil {
|
|
if links == nil {
|
|
t.Error("GetLinks() returned nil slice without error")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGetRoutes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
insecure bool
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "get routes in insecure mode",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary and running node")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
routes, err := tc.GetRoutes(tt.nodeIP, tt.insecure)
|
|
|
|
if err == nil {
|
|
if routes == nil {
|
|
t.Error("GetRoutes() returned nil slice without error")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGetDefaultInterface(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
insecure bool
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "get default interface",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary and running node")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
iface, err := tc.GetDefaultInterface(tt.nodeIP, tt.insecure)
|
|
|
|
if err == nil {
|
|
if iface == "" {
|
|
t.Error("GetDefaultInterface() returned empty string without error")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGetPhysicalInterface(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
insecure bool
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "get physical interface",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary and running node")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
iface, err := tc.GetPhysicalInterface(tt.nodeIP, tt.insecure)
|
|
|
|
if err == nil {
|
|
if iface == "" {
|
|
t.Error("GetPhysicalInterface() returned empty string without error")
|
|
}
|
|
// Should not be loopback
|
|
if iface == "lo" {
|
|
t.Error("GetPhysicalInterface() returned loopback interface")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigGetVersion(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
nodeIP string
|
|
insecure bool
|
|
want string // Expected for maintenance mode or version string
|
|
skipTest bool
|
|
}{
|
|
{
|
|
name: "get version in insecure mode",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: true,
|
|
skipTest: true,
|
|
},
|
|
{
|
|
name: "get version in secure mode",
|
|
nodeIP: "192.168.1.100",
|
|
insecure: false,
|
|
skipTest: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.skipTest {
|
|
t.Skip("Skipping test that requires talosctl binary and running node")
|
|
}
|
|
|
|
tc := NewTalosctl()
|
|
version, err := tc.GetVersion(tt.nodeIP, tt.insecure)
|
|
|
|
if err == nil {
|
|
if version == "" {
|
|
t.Error("GetVersion() returned empty string without error")
|
|
}
|
|
// Version should be either "maintenance" or start with "v"
|
|
if version != "maintenance" && version[0] != 'v' {
|
|
t.Errorf("GetVersion() = %q, expected 'maintenance' or version starting with 'v'", version)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestTalosconfigValidate(t *testing.T) {
|
|
t.Run("validate checks for talosctl", func(t *testing.T) {
|
|
tc := NewTalosctl()
|
|
err := tc.Validate()
|
|
|
|
// This will pass if talosctl is installed, fail otherwise
|
|
// We can't guarantee talosctl is installed in all test environments
|
|
_ = err
|
|
})
|
|
}
|
|
|
|
func TestDiskInfoStruct(t *testing.T) {
|
|
t.Run("DiskInfo has required fields", func(t *testing.T) {
|
|
disk := DiskInfo{
|
|
Path: "/dev/sda",
|
|
Size: 1000000000000, // 1TB
|
|
}
|
|
|
|
if disk.Path != "/dev/sda" {
|
|
t.Errorf("Path = %q, want %q", disk.Path, "/dev/sda")
|
|
}
|
|
if disk.Size != 1000000000000 {
|
|
t.Errorf("Size = %d, want %d", disk.Size, 1000000000000)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestTalosconfigResourceJSONParsing(t *testing.T) {
|
|
// This test verifies the logic of getResourceJSON without actually calling talosctl
|
|
t.Run("getResourceJSON uses correct command structure", func(t *testing.T) {
|
|
tc := &Talosctl{talosconfigPath: "/path/to/talosconfig"}
|
|
|
|
// We can't easily test the actual command execution without mocking,
|
|
// but we can verify buildArgs works correctly
|
|
baseArgs := []string{"get", "disks", "--nodes", "192.168.1.100", "-o", "json"}
|
|
finalArgs := tc.buildArgs(baseArgs)
|
|
|
|
// Should have talosconfig prepended
|
|
if len(finalArgs) < 2 || finalArgs[0] != "--talosconfig" {
|
|
t.Error("buildArgs() should prepend --talosconfig")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestTalosconfigInterfaceFiltering(t *testing.T) {
|
|
// Test the logic for filtering physical interfaces
|
|
tests := []struct {
|
|
name string
|
|
interfaceName string
|
|
linkType string
|
|
operState string
|
|
shouldAccept bool
|
|
}{
|
|
{
|
|
name: "eth0 up and ethernet",
|
|
interfaceName: "eth0",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: true,
|
|
},
|
|
{
|
|
name: "eno1 up and ethernet",
|
|
interfaceName: "eno1",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: true,
|
|
},
|
|
{
|
|
name: "loopback should be filtered",
|
|
interfaceName: "lo",
|
|
linkType: "loopback",
|
|
operState: "up",
|
|
shouldAccept: false,
|
|
},
|
|
{
|
|
name: "cni interface should be filtered",
|
|
interfaceName: "cni0",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: false,
|
|
},
|
|
{
|
|
name: "flannel interface should be filtered",
|
|
interfaceName: "flannel.1",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: false,
|
|
},
|
|
{
|
|
name: "docker interface should be filtered",
|
|
interfaceName: "docker0",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: false,
|
|
},
|
|
{
|
|
name: "bridge interface should be filtered",
|
|
interfaceName: "br-1234",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: false,
|
|
},
|
|
{
|
|
name: "veth interface should be filtered",
|
|
interfaceName: "veth123",
|
|
linkType: "ether",
|
|
operState: "up",
|
|
shouldAccept: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// This simulates the filtering logic in GetPhysicalInterface
|
|
id := tt.interfaceName
|
|
linkType := tt.linkType
|
|
operState := tt.operState
|
|
|
|
shouldAccept := (linkType == "ether" && operState == "up" &&
|
|
id != "lo" &&
|
|
(id[:3] == "eth" || id[:2] == "en") &&
|
|
!containsAny(id, []string{"cni", "flannel", "docker", "br-", "veth"}))
|
|
|
|
if shouldAccept != tt.shouldAccept {
|
|
t.Errorf("Interface %q filtering = %v, want %v", id, shouldAccept, tt.shouldAccept)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper function for interface filtering test
|
|
func containsAny(s string, substrs []string) bool {
|
|
for _, substr := range substrs {
|
|
if len(substr) > 0 {
|
|
if substr[len(substr)-1] == '-' {
|
|
// Prefix match for things like "br-"
|
|
if len(s) >= len(substr) && s[:len(substr)] == substr {
|
|
return true
|
|
}
|
|
} else {
|
|
// Contains match
|
|
if len(s) >= len(substr) {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|