From a2c8d364aa0e01f3bf828de8edc728d2e4e3d518 Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Thu, 13 Nov 2025 19:35:39 -0500 Subject: [PATCH] fix(workstation): Correctly generate service DNS entries Explicitly sets services on the network manager. This change fixes a regression in which the `Corefile` was missing service entries. Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- pkg/pipelines/down.go | 16 +++- pkg/pipelines/down_test.go | 9 +- pkg/pipelines/init.go | 2 +- pkg/pipelines/up.go | 14 ++- pkg/pipelines/up_test.go | 7 +- pkg/project/project.go | 2 +- pkg/workstation/network/colima_network.go | 5 +- .../network/colima_network_test.go | 26 +++--- .../network/darwin_network_test.go | 6 +- pkg/workstation/network/linux_network_test.go | 6 +- pkg/workstation/network/mock_network.go | 12 ++- pkg/workstation/network/mock_network_test.go | 8 +- pkg/workstation/network/network.go | 23 ++--- pkg/workstation/network/network_test.go | 88 +++++++++++++------ .../network/windows_network_test.go | 14 ++- .../services/talos_service_test.go | 2 +- pkg/workstation/workstation.go | 54 +++++++----- 17 files changed, 176 insertions(+), 118 deletions(-) diff --git a/pkg/pipelines/down.go b/pkg/pipelines/down.go index 9907306a7..6d5cb3ed4 100644 --- a/pkg/pipelines/down.go +++ b/pkg/pipelines/down.go @@ -6,14 +6,15 @@ import ( "os" "path/filepath" - "github.com/windsorcli/cli/pkg/di" + "github.com/windsorcli/cli/pkg/composer/blueprint" envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/provisioner/kubernetes" k8sclient "github.com/windsorcli/cli/pkg/provisioner/kubernetes/client" terraforminfra "github.com/windsorcli/cli/pkg/provisioner/terraform" - "github.com/windsorcli/cli/pkg/composer/blueprint" - "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" + "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" ) @@ -103,7 +104,14 @@ func (p *DownPipeline) Initialize(injector di.Injector, ctx context.Context) err } if p.networkManager != nil { - if err := p.networkManager.Initialize(); err != nil { + resolvedServices, _ := p.injector.ResolveAll(new(services.Service)) + serviceList := make([]services.Service, 0, len(resolvedServices)) + for _, svc := range resolvedServices { + if s, ok := svc.(services.Service); ok { + serviceList = append(serviceList, s) + } + } + if err := p.networkManager.Initialize(serviceList); err != nil { return fmt.Errorf("failed to initialize network manager: %w", err) } } diff --git a/pkg/pipelines/down_test.go b/pkg/pipelines/down_test.go index a5ae3a83e..0a39e81b8 100644 --- a/pkg/pipelines/down_test.go +++ b/pkg/pipelines/down_test.go @@ -8,13 +8,14 @@ import ( "testing" blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" + "github.com/windsorcli/cli/pkg/composer/blueprint" "github.com/windsorcli/cli/pkg/context/config" envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/provisioner/kubernetes" terraforminfra "github.com/windsorcli/cli/pkg/provisioner/terraform" - "github.com/windsorcli/cli/pkg/composer/blueprint" - "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" + "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" ) @@ -84,7 +85,7 @@ contexts: // Setup network manager mock mockNetworkManager := network.NewMockNetworkManager() - mockNetworkManager.InitializeFunc = func() error { return nil } + mockNetworkManager.InitializeFunc = func([]services.Service) error { return nil } baseMocks.Injector.Register("networkManager", mockNetworkManager) // Setup stack mock @@ -382,7 +383,7 @@ func TestDownPipeline_Initialize(t *testing.T) { mocks := setupDownMocks(t) // Set up a failing network manager mock - mocks.NetworkManager.InitializeFunc = func() error { + mocks.NetworkManager.InitializeFunc = func([]services.Service) error { return fmt.Errorf("network manager initialization failed") } diff --git a/pkg/pipelines/init.go b/pkg/pipelines/init.go index babc03337..ddd745fc5 100644 --- a/pkg/pipelines/init.go +++ b/pkg/pipelines/init.go @@ -198,7 +198,7 @@ func (p *InitPipeline) Initialize(injector di.Injector, ctx context.Context) err } if p.networkManager != nil { - if err := p.networkManager.Initialize(); err != nil { + if err := p.networkManager.Initialize(p.services); err != nil { return fmt.Errorf("failed to initialize network manager: %w", err) } } diff --git a/pkg/pipelines/up.go b/pkg/pipelines/up.go index 78cdc3d0d..a1ee54692 100644 --- a/pkg/pipelines/up.go +++ b/pkg/pipelines/up.go @@ -5,12 +5,13 @@ import ( "fmt" "os" - "github.com/windsorcli/cli/pkg/di" envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/context/tools" + "github.com/windsorcli/cli/pkg/di" terraforminfra "github.com/windsorcli/cli/pkg/provisioner/terraform" - "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" + "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" ) @@ -105,7 +106,14 @@ func (p *UpPipeline) Initialize(injector di.Injector, ctx context.Context) error } if p.networkManager != nil { - if err := p.networkManager.Initialize(); err != nil { + resolvedServices, _ := p.injector.ResolveAll(new(services.Service)) + serviceList := make([]services.Service, 0, len(resolvedServices)) + for _, svc := range resolvedServices { + if s, ok := svc.(services.Service); ok { + serviceList = append(serviceList, s) + } + } + if err := p.networkManager.Initialize(serviceList); err != nil { return fmt.Errorf("failed to initialize network manager: %w", err) } } diff --git a/pkg/pipelines/up_test.go b/pkg/pipelines/up_test.go index cd2539a3e..92cd73d31 100644 --- a/pkg/pipelines/up_test.go +++ b/pkg/pipelines/up_test.go @@ -8,10 +8,11 @@ import ( blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" "github.com/windsorcli/cli/pkg/context/config" envvars "github.com/windsorcli/cli/pkg/context/env" + "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/context/tools" terraforminfra "github.com/windsorcli/cli/pkg/provisioner/terraform" - "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/workstation/network" + "github.com/windsorcli/cli/pkg/workstation/services" "github.com/windsorcli/cli/pkg/workstation/virt" ) @@ -87,7 +88,7 @@ contexts: // Setup network manager mock mockNetworkManager := network.NewMockNetworkManager() - mockNetworkManager.InitializeFunc = func() error { return nil } + mockNetworkManager.InitializeFunc = func([]services.Service) error { return nil } mockNetworkManager.ConfigureGuestFunc = func() error { return nil } mockNetworkManager.ConfigureHostRouteFunc = func() error { return nil } mockNetworkManager.ConfigureDNSFunc = func() error { return nil } @@ -206,7 +207,7 @@ func TestUpPipeline_Initialize(t *testing.T) { { name: "ReturnsErrorWhenNetworkManagerInitializeFails", setupMock: func(mocks *UpMocks) { - mocks.NetworkManager.InitializeFunc = func() error { + mocks.NetworkManager.InitializeFunc = func([]services.Service) error { return fmt.Errorf("network manager failed") } }, diff --git a/pkg/project/project.go b/pkg/project/project.go index 60b335405..1741bde73 100644 --- a/pkg/project/project.go +++ b/pkg/project/project.go @@ -137,7 +137,7 @@ func (p *Project) Configure(flagOverrides map[string]any) error { // if any step fails. func (p *Project) Initialize(overwrite bool) error { if p.Workstation != nil && p.Workstation.NetworkManager != nil { - if err := p.Workstation.NetworkManager.Initialize(); err != nil { + if err := p.Workstation.NetworkManager.Initialize(p.Workstation.Services); err != nil { return fmt.Errorf("failed to initialize network manager: %w", err) } } diff --git a/pkg/workstation/network/colima_network.go b/pkg/workstation/network/colima_network.go index 010a6bd84..b40b6a28e 100644 --- a/pkg/workstation/network/colima_network.go +++ b/pkg/workstation/network/colima_network.go @@ -9,6 +9,7 @@ import ( "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/context/shell/ssh" + "github.com/windsorcli/cli/pkg/workstation/services" ) // The ColimaNetworkManager is a specialized network manager for Colima-based environments. @@ -47,8 +48,8 @@ func NewColimaNetworkManager(injector di.Injector) *ColimaNetworkManager { // Initialize sets up the ColimaNetworkManager by resolving dependencies for // sshClient, shell, and secureShell from the injector. -func (n *ColimaNetworkManager) Initialize() error { - if err := n.BaseNetworkManager.Initialize(); err != nil { +func (n *ColimaNetworkManager) Initialize(services []services.Service) error { + if err := n.BaseNetworkManager.Initialize(services); err != nil { return err } diff --git a/pkg/workstation/network/colima_network_test.go b/pkg/workstation/network/colima_network_test.go index a5ae227a0..2960605c2 100644 --- a/pkg/workstation/network/colima_network_test.go +++ b/pkg/workstation/network/colima_network_test.go @@ -5,6 +5,8 @@ import ( "net" "strings" "testing" + + "github.com/windsorcli/cli/pkg/workstation/services" ) // ============================================================================= @@ -26,7 +28,7 @@ func TestColimaNetworkManager_Initialize(t *testing.T) { mocks.Injector.Register("secureShell", "invalid") // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { @@ -45,7 +47,7 @@ func TestColimaNetworkManager_Initialize(t *testing.T) { mocks.Injector.Register("sshClient", "invalid") // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { @@ -64,7 +66,7 @@ func TestColimaNetworkManager_Initialize(t *testing.T) { mocks.Injector.Register("networkInterfaceProvider", "invalid") // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { @@ -84,7 +86,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { mocks := setupMocks(t) manager := NewColimaNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } @@ -148,7 +150,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -174,7 +176,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -203,7 +205,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -232,7 +234,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -267,7 +269,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -293,7 +295,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -322,7 +324,7 @@ func TestColimaNetworkManager_ConfigureGuest(t *testing.T) { } // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) if err != nil { t.Fatalf("expected no error during initialization, got %v", err) } @@ -345,7 +347,7 @@ func TestColimaNetworkManager_getHostIP(t *testing.T) { t.Helper() mocks := setupMocks(t) manager := NewColimaNetworkManager(mocks.Injector) - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } diff --git a/pkg/workstation/network/darwin_network_test.go b/pkg/workstation/network/darwin_network_test.go index c16f92502..e2e5ae72d 100644 --- a/pkg/workstation/network/darwin_network_test.go +++ b/pkg/workstation/network/darwin_network_test.go @@ -7,6 +7,8 @@ import ( "fmt" "os" "testing" + + "github.com/windsorcli/cli/pkg/workstation/services" ) // ============================================================================= @@ -19,7 +21,7 @@ func TestDarwinNetworkManager_ConfigureHostRoute(t *testing.T) { mocks := setupMocks(t) manager := NewBaseNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } @@ -167,7 +169,7 @@ func TestDarwinNetworkManager_ConfigureDNS(t *testing.T) { mocks := setupMocks(t) manager := NewBaseNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } diff --git a/pkg/workstation/network/linux_network_test.go b/pkg/workstation/network/linux_network_test.go index f3c9dc437..c9478e7e4 100644 --- a/pkg/workstation/network/linux_network_test.go +++ b/pkg/workstation/network/linux_network_test.go @@ -8,6 +8,8 @@ import ( "os" "strings" "testing" + + "github.com/windsorcli/cli/pkg/workstation/services" ) // ============================================================================= @@ -20,7 +22,7 @@ func TestLinuxNetworkManager_ConfigureHostRoute(t *testing.T) { mocks := setupMocks(t) manager := NewBaseNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } @@ -169,7 +171,7 @@ func TestLinuxNetworkManager_ConfigureDNS(t *testing.T) { mocks := setupMocks(t) manager := NewBaseNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } diff --git a/pkg/workstation/network/mock_network.go b/pkg/workstation/network/mock_network.go index 406b5be7f..7f81a4beb 100644 --- a/pkg/workstation/network/mock_network.go +++ b/pkg/workstation/network/mock_network.go @@ -1,6 +1,10 @@ package network -import "net" +import ( + "net" + + "github.com/windsorcli/cli/pkg/workstation/services" +) // The MockNetworkManager is a test implementation of the NetworkManager interface. // It provides mock implementations of network management functions for testing, @@ -14,7 +18,7 @@ import "net" // MockNetworkManager is a struct that simulates a network manager for testing purposes. type MockNetworkManager struct { NetworkManager - InitializeFunc func() error + InitializeFunc func([]services.Service) error ConfigureHostRouteFunc func() error ConfigureGuestFunc func() error ConfigureDNSFunc func() error @@ -34,9 +38,9 @@ func NewMockNetworkManager() *MockNetworkManager { // ============================================================================= // Initialize calls the custom InitializeFunc if provided. -func (m *MockNetworkManager) Initialize() error { +func (m *MockNetworkManager) Initialize(services []services.Service) error { if m.InitializeFunc != nil { - return m.InitializeFunc() + return m.InitializeFunc(services) } return nil } diff --git a/pkg/workstation/network/mock_network_test.go b/pkg/workstation/network/mock_network_test.go index b926db4d1..6603434cb 100644 --- a/pkg/workstation/network/mock_network_test.go +++ b/pkg/workstation/network/mock_network_test.go @@ -2,6 +2,8 @@ package network import ( "testing" + + "github.com/windsorcli/cli/pkg/workstation/services" ) // ============================================================================= @@ -12,12 +14,12 @@ func TestMockNetworkManager_Initialize(t *testing.T) { t.Run("Success", func(t *testing.T) { // Given a mock network manager with successful initialization mockManager := NewMockNetworkManager() - mockManager.InitializeFunc = func() error { + mockManager.InitializeFunc = func([]services.Service) error { return nil } // When initializing the manager - err := mockManager.Initialize() + err := mockManager.Initialize([]services.Service{}) // Then no error should occur if err != nil { @@ -30,7 +32,7 @@ func TestMockNetworkManager_Initialize(t *testing.T) { mockManager := NewMockNetworkManager() // When initializing the manager - err := mockManager.Initialize() + err := mockManager.Initialize([]services.Service{}) // Then no error should occur if err != nil { diff --git a/pkg/workstation/network/network.go b/pkg/workstation/network/network.go index ef63a4b5e..ef220c78d 100644 --- a/pkg/workstation/network/network.go +++ b/pkg/workstation/network/network.go @@ -5,11 +5,11 @@ import ( "net" "sort" - "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/constants" - "github.com/windsorcli/cli/pkg/di" + "github.com/windsorcli/cli/pkg/context/config" "github.com/windsorcli/cli/pkg/context/shell" "github.com/windsorcli/cli/pkg/context/shell/ssh" + "github.com/windsorcli/cli/pkg/di" "github.com/windsorcli/cli/pkg/workstation/services" ) @@ -24,7 +24,7 @@ import ( // NetworkManager handles configuring the local development network type NetworkManager interface { - Initialize() error + Initialize(services []services.Service) error ConfigureHostRoute() error ConfigureGuest() error ConfigureDNS() error @@ -58,8 +58,9 @@ func NewBaseNetworkManager(injector di.Injector) *BaseNetworkManager { // Public Methods // ============================================================================= -// Initialize resolves dependencies, sorts services, and assigns IPs based on network CIDR -func (n *BaseNetworkManager) Initialize() error { +// Initialize resolves dependencies, sorts services, and assigns IPs based on network CIDR. +// Services are passed explicitly from Workstation to ensure we work with the same instances. +func (n *BaseNetworkManager) Initialize(serviceList []services.Service) error { shellInterface, ok := n.injector.Resolve("shell").(shell.Shell) if !ok { return fmt.Errorf("resolved shell instance is not of type shell.Shell") @@ -72,17 +73,7 @@ func (n *BaseNetworkManager) Initialize() error { } n.configHandler = configHandler - resolvedServices, err := n.injector.ResolveAll(new(services.Service)) - if err != nil { - return fmt.Errorf("error resolving services: %w", err) - } - - var serviceList []services.Service - for _, serviceInterface := range resolvedServices { - service, _ := serviceInterface.(services.Service) - serviceList = append(serviceList, service) - } - + // Sort services by name for consistent IP assignment sort.Slice(serviceList, func(i, j int) bool { return serviceList[i].GetName() < serviceList[j].GetName() }) diff --git a/pkg/workstation/network/network_test.go b/pkg/workstation/network/network_test.go index d03736635..82d747e26 100644 --- a/pkg/workstation/network/network_test.go +++ b/pkg/workstation/network/network_test.go @@ -260,8 +260,14 @@ func TestNetworkManager_Initialize(t *testing.T) { return nil } + // Convert mock services to service interface slice + serviceList := make([]services.Service, len(mocks.Services)) + for i, s := range mocks.Services { + serviceList[i] = s + } + // When creating and initializing the network manager - err := manager.Initialize() + err := manager.Initialize(serviceList) // Then no error should occur if err != nil { @@ -290,8 +296,14 @@ func TestNetworkManager_Initialize(t *testing.T) { return fmt.Errorf("mock error setting address for service") } + // Convert mock services to service interface slice + serviceList := make([]services.Service, len(mocks.Services)) + for i, s := range mocks.Services { + serviceList[i] = s + } + // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize(serviceList) // Then an error should occur if err == nil { @@ -299,7 +311,7 @@ func TestNetworkManager_Initialize(t *testing.T) { } // And the error should contain the expected message - expectedErrorSubstring := "error setting address for service" + expectedErrorSubstring := "error assigning IP addresses" if !strings.Contains(err.Error(), expectedErrorSubstring) { t.Errorf("expected error message to contain %q, got %q", expectedErrorSubstring, err.Error()) } @@ -311,7 +323,7 @@ func TestNetworkManager_Initialize(t *testing.T) { mocks.Injector.Register("shell", "invalid") // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { @@ -330,7 +342,7 @@ func TestNetworkManager_Initialize(t *testing.T) { mocks.Injector.Register("configHandler", "invalid") // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { @@ -344,38 +356,54 @@ func TestNetworkManager_Initialize(t *testing.T) { }) t.Run("ErrorResolvingServices", func(t *testing.T) { - // Given a network manager with service resolution error - mockInjector := di.NewMockInjector() - setupMocks(t, &SetupOptions{ - Injector: mockInjector, - }) - manager := NewBaseNetworkManager(mockInjector) - mockInjector.SetResolveAllError(new(services.Service), fmt.Errorf("mock error resolving services")) + // Given a network manager + manager, mocks := setup(t) - // When initializing the network manager - err := manager.Initialize() + // When initializing the network manager with services + // (services are now passed explicitly, so no resolution error can occur) + err := manager.Initialize([]services.Service{}) - // Then an error should occur - if err == nil { - t.Errorf("expected error, got none") + // Then no error should occur (services are passed directly, not resolved) + if err != nil { + t.Errorf("expected no error when services are passed explicitly, got %v", err) } - // And the error should contain the expected message - expectedErrorSubstring := "error resolving services" - if !strings.Contains(err.Error(), expectedErrorSubstring) { - t.Errorf("expected error message to contain %q, got %q", expectedErrorSubstring, err.Error()) + // Verify manager was initialized + if manager.configHandler == nil { + t.Error("expected configHandler to be set") + } + if manager.shell == nil { + t.Error("expected shell to be set") } + _ = mocks // suppress unused variable warning }) t.Run("ErrorSettingNetworkCidr", func(t *testing.T) { // Given a network manager with CIDR setting error - manager, mocks := setup(t) - mocks.Services[0].SetAddressFunc = func(address string, portAllocator *services.PortAllocator) error { - return fmt.Errorf("error setting default network CIDR") + // Create a mock config handler that returns error for Set + mockConfigHandler := config.NewMockConfigHandler() + mockConfigHandler.GetStringFunc = func(key string, defaultValue ...string) string { + if key == "network.cidr_block" { + return "" // Return empty so Set is called + } + return "10.5.0.0/16" } + mockConfigHandler.SetFunc = func(key string, value any) error { + if key == "network.cidr_block" { + return fmt.Errorf("error setting default network CIDR") + } + return nil + } + + // Setup with mock config handler + mocks := setupMocks(t, &SetupOptions{ + ConfigHandler: mockConfigHandler, + }) + manager := NewBaseNetworkManager(mocks.Injector) + manager.shims = mocks.Shims // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { @@ -392,12 +420,15 @@ func TestNetworkManager_Initialize(t *testing.T) { t.Run("ErrorAssigningIPAddresses", func(t *testing.T) { // Given a network manager with IP assignment error manager, mocks := setup(t) - mocks.Services[0].SetAddressFunc = func(address string, portAllocator *services.PortAllocator) error { + // Create a service with SetAddress that returns error + mockService := services.NewMockService() + mockService.SetAddressFunc = func(address string, portAllocator *services.PortAllocator) error { return fmt.Errorf("mock assign IP addresses error") } + serviceList := []services.Service{mockService} // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize(serviceList) // Then an error should occur if err == nil { @@ -409,6 +440,7 @@ func TestNetworkManager_Initialize(t *testing.T) { if !strings.Contains(err.Error(), expectedErrorSubstring) { t.Errorf("expected error message to contain %q, got %q", expectedErrorSubstring, err.Error()) } + _ = mocks // suppress unused variable warning }) t.Run("ResolveShellFailure", func(t *testing.T) { @@ -417,7 +449,7 @@ func TestNetworkManager_Initialize(t *testing.T) { mocks.Injector.Register("shell", "invalid") // When initializing the network manager - err := manager.Initialize() + err := manager.Initialize([]services.Service{}) // Then an error should occur if err == nil { diff --git a/pkg/workstation/network/windows_network_test.go b/pkg/workstation/network/windows_network_test.go index 12e69aaa3..0ce006a1f 100644 --- a/pkg/workstation/network/windows_network_test.go +++ b/pkg/workstation/network/windows_network_test.go @@ -7,6 +7,8 @@ import ( "fmt" "strings" "testing" + + "github.com/windsorcli/cli/pkg/workstation/services" ) // ============================================================================= @@ -19,7 +21,7 @@ func TestWindowsNetworkManager_ConfigureHostRoute(t *testing.T) { mocks := setupMocks(t) manager := NewBaseNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } @@ -27,14 +29,8 @@ func TestWindowsNetworkManager_ConfigureHostRoute(t *testing.T) { // Given a properly configured network manager manager, _ := setup(t) - // When initializing the network manager - err := manager.Initialize() - if err != nil { - t.Fatalf("expected no error during initialization, got %v", err) - } - // And configuring the host route - err = manager.ConfigureHostRoute() + err := manager.ConfigureHostRoute() // Then no error should occur if err != nil { @@ -148,7 +144,7 @@ func TestWindowsNetworkManager_ConfigureDNS(t *testing.T) { mocks := setupMocks(t) manager := NewBaseNetworkManager(mocks.Injector) manager.shims = mocks.Shims - manager.Initialize() + manager.Initialize([]services.Service{}) return manager, mocks } diff --git a/pkg/workstation/services/talos_service_test.go b/pkg/workstation/services/talos_service_test.go index 09186e6ef..7dae6b420 100644 --- a/pkg/workstation/services/talos_service_test.go +++ b/pkg/workstation/services/talos_service_test.go @@ -638,7 +638,7 @@ func TestTalosService_SetAddress(t *testing.T) { } // When SetAddress is called for third worker - if err := service3.SetAddress("192.168.1.22", portAllocator); err != nil { + if err := service3.SetAddress("192.168.1.22", nil); err != nil { t.Fatalf("Failed to set address for service3: %v", err) } diff --git a/pkg/workstation/workstation.go b/pkg/workstation/workstation.go index 7caab0fc5..3f4be4215 100644 --- a/pkg/workstation/workstation.go +++ b/pkg/workstation/workstation.go @@ -77,11 +77,11 @@ func NewWorkstation(ctx *WorkstationExecutionContext, injector di.Injector) (*Wo // Create Services if not already set if workstation.Services == nil { - services, err := workstation.createServices() + serviceList, err := workstation.createServices() if err != nil { return nil, fmt.Errorf("failed to create services: %w", err) } - workstation.Services = services + workstation.Services = serviceList } // Create VirtualMachine if not already set @@ -105,11 +105,6 @@ func NewWorkstation(ctx *WorkstationExecutionContext, injector di.Injector) (*Wo workstation.SSHClient = ssh.NewSSHClient() } - // Initialize NetworkManager to assign IP addresses to services - if err := workstation.NetworkManager.Initialize(); err != nil { - return nil, fmt.Errorf("failed to initialize network manager: %w", err) - } - return workstation, nil } @@ -118,13 +113,15 @@ func NewWorkstation(ctx *WorkstationExecutionContext, injector di.Injector) (*Wo // ============================================================================= // Up starts the workstation environment including VMs, containers, networking, and services. +// It sets the NO_CACHE environment variable, launches the virtual machine if the driver is "colima", +// initializes the network manager for all registered services, re-initializes DNS services, +// writes service configurations, initializes and starts the container runtime if enabled, +// configures networking components, and informs the user of successful environment setup. func (w *Workstation) Up() error { - // Set NO_CACHE environment variable to prevent caching during up operations if err := os.Setenv("NO_CACHE", "true"); err != nil { return fmt.Errorf("Error setting NO_CACHE environment variable: %w", err) } - // Start virtual machine if using colima vmDriver := w.ConfigHandler.GetString("vm.driver") if vmDriver == "colima" { if w.VirtualMachine == nil { @@ -138,7 +135,27 @@ func (w *Workstation) Up() error { } } - // Start container runtime if enabled + if w.NetworkManager != nil { + if err := w.NetworkManager.Initialize(w.Services); err != nil { + return fmt.Errorf("failed to initialize network manager: %w", err) + } + } + + for _, service := range w.Services { + if dnsService, ok := service.(*services.DNSService); ok { + if err := dnsService.Initialize(); err != nil { + return fmt.Errorf("failed to re-initialize DNS service: %w", err) + } + break + } + } + + for _, service := range w.Services { + if err := service.WriteConfig(); err != nil { + return fmt.Errorf("Error writing config for service %s: %w", service.GetName(), err) + } + } + containerRuntimeEnabled := w.ConfigHandler.GetBool("docker.enabled") if containerRuntimeEnabled { if w.ContainerRuntime == nil { @@ -155,7 +172,6 @@ func (w *Workstation) Up() error { } } - // Configure networking if w.NetworkManager != nil { // Only configure guest and host routes for colima if vmDriver == "colima" { @@ -175,36 +191,28 @@ func (w *Workstation) Up() error { } } - // Write service configurations - for _, service := range w.Services { - if err := service.WriteConfig(); err != nil { - return fmt.Errorf("Error writing config for service %s: %w", service.GetName(), err) - } - } - - // Print success message fmt.Fprintln(os.Stderr, "Windsor environment set up successfully.") return nil } -// Down stops the workstation environment including services, containers, VMs, and networking. +// Down stops the workstation environment, including services, containers, VMs, and networking. +// It attempts to gracefully shut down the container runtime and virtual machine if they exist. +// On success, it prints a confirmation message to standard error and returns nil. If any teardown +// step fails, it returns an error describing the issue. func (w *Workstation) Down() error { - // Stop container runtime if w.ContainerRuntime != nil { if err := w.ContainerRuntime.Down(); err != nil { return fmt.Errorf("Error running container runtime Down command: %w", err) } } - // Stop virtual machine if w.VirtualMachine != nil { if err := w.VirtualMachine.Down(); err != nil { return fmt.Errorf("Error running virtual machine Down command: %w", err) } } - // Print success message fmt.Fprintln(os.Stderr, "Windsor environment torn down successfully.") return nil