Files
wild-central-api/internal/tools/talosctl_test.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
}