From 4e6c7ac8e8d6ebd9bf1950c0c5f36beadfde56a7 Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Thu, 13 Nov 2025 20:39:05 -0500 Subject: [PATCH 1/2] refactor(cmd): Remove runtime and pipeline from install command Removed the deprecated runtime and pipeline constructs in favor of the new top level components. Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- cmd/install.go | 53 ++++++++---------- cmd/install_test.go | 128 ++++++++++++++++++++------------------------ 2 files changed, 81 insertions(+), 100 deletions(-) diff --git a/cmd/install.go b/cmd/install.go index cf1df05f8..bee132423 100644 --- a/cmd/install.go +++ b/cmd/install.go @@ -1,13 +1,11 @@ package cmd import ( - "context" "fmt" "github.com/spf13/cobra" "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/pipelines" - "github.com/windsorcli/cli/pkg/runtime" + "github.com/windsorcli/cli/pkg/project" ) var installWaitFlag bool @@ -17,42 +15,37 @@ var installCmd = &cobra.Command{ Short: "Install the blueprint's cluster-level services", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - // Get shared dependency injector from context injector := cmd.Context().Value(injectorKey).(di.Injector) - // First, set up environment variables using runtime - deps := &runtime.Dependencies{ - Injector: injector, + proj, err := project.NewProject(injector, "") + if err != nil { + return err } - if err := runtime.NewRuntime(deps). - LoadShell(). - CheckTrustedDirectory(). - LoadConfig(). - LoadSecretsProviders(). - LoadEnvVars(runtime.EnvVarsOptions{ - Decrypt: true, - Verbose: verbose, - }). - ExecutePostEnvHook(verbose). - Do(); err != nil { - return fmt.Errorf("failed to set up environment: %w", err) + + if err := proj.Context.Shell.CheckTrustedDirectory(); err != nil { + return fmt.Errorf("not in a trusted directory. If you are in a Windsor project, run 'windsor init' to approve") } - // Then, run the install pipeline for blueprint installation - installPipeline, err := pipelines.WithPipeline(injector, cmd.Context(), "installPipeline") - if err != nil { - return fmt.Errorf("failed to set up install pipeline: %w", err) + if err := proj.Configure(nil); err != nil { + return err } - // Create execution context with flags - ctx := cmd.Context() - if installWaitFlag { - ctx = context.WithValue(ctx, "wait", true) + if err := proj.Initialize(false); err != nil { + if !verbose { + return nil + } + return err } - // Execute the install pipeline - if err := installPipeline.Execute(ctx); err != nil { - return fmt.Errorf("Error executing install pipeline: %w", err) + blueprint := proj.Composer.BlueprintHandler.Generate() + if err := proj.Provisioner.Install(blueprint); err != nil { + return fmt.Errorf("error installing blueprint: %w", err) + } + + if installWaitFlag { + if err := proj.Provisioner.Wait(blueprint); err != nil { + return fmt.Errorf("error waiting for kustomizations: %w", err) + } } return nil diff --git a/cmd/install_test.go b/cmd/install_test.go index 8179c66c9..fd43154f0 100644 --- a/cmd/install_test.go +++ b/cmd/install_test.go @@ -8,56 +8,25 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "github.com/windsorcli/cli/pkg/di" - "github.com/windsorcli/cli/pkg/pipelines" + blueprintv1alpha1 "github.com/windsorcli/cli/api/v1alpha1" + "github.com/windsorcli/cli/pkg/composer/blueprint" + "github.com/windsorcli/cli/pkg/context/config" + terraforminfra "github.com/windsorcli/cli/pkg/provisioner/terraform" + "github.com/windsorcli/cli/pkg/workstation/virt" ) -// ============================================================================= -// Test Setup -// ============================================================================= - -type InstallMocks struct { - *Mocks -} - -func setupInstallTest(t *testing.T, opts ...*SetupOptions) *InstallMocks { - t.Helper() - - // Setup base mocks - baseMocks := setupMocks(t, opts...) - - // Note: envPipeline no longer used - install now uses runtime.LoadEnvVars - - // Register mock install pipeline in injector - mockInstallPipeline := pipelines.NewMockBasePipeline() - mockInstallPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil } - mockInstallPipeline.ExecuteFunc = func(ctx context.Context) error { return nil } - baseMocks.Injector.Register("installPipeline", mockInstallPipeline) - - return &InstallMocks{ - Mocks: baseMocks, - } -} - -// ============================================================================= -// Test Cases -// ============================================================================= - func TestInstallCmd(t *testing.T) { createTestInstallCmd := func() *cobra.Command { - // Create a new command with the same RunE as installCmd cmd := &cobra.Command{ Use: "install", Short: "Install the blueprint's cluster-level services", RunE: installCmd.RunE, } - // Copy all flags from installCmd to ensure they're available installCmd.Flags().VisitAll(func(flag *pflag.Flag) { cmd.Flags().AddFlag(flag) }) - // Disable help text printing cmd.SilenceUsage = true cmd.SilenceErrors = true @@ -65,84 +34,103 @@ func TestInstallCmd(t *testing.T) { } t.Run("Success", func(t *testing.T) { - // Given a temporary directory with mocked dependencies - mocks := setupInstallTest(t) + mocks := setupMocks(t) + + mockBlueprintHandler := blueprint.NewMockBlueprintHandler(mocks.Injector) + mockBlueprintHandler.GenerateFunc = func() *blueprintv1alpha1.Blueprint { + return &blueprintv1alpha1.Blueprint{} + } + mocks.Injector.Register("blueprintHandler", mockBlueprintHandler) + + mockStack := &terraforminfra.MockStack{} + mockStack.InitializeFunc = func() error { return nil } + mocks.Injector.Register("stack", mockStack) + + mockContainerRuntime := &virt.MockVirt{} + mockContainerRuntime.InitializeFunc = func() error { return nil } + mocks.Injector.Register("containerRuntime", mockContainerRuntime) - // When executing the install command cmd := createTestInstallCmd() ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector) cmd.SetArgs([]string{}) cmd.SetContext(ctx) err := cmd.Execute() - // Then no error should occur if err != nil { t.Errorf("Expected success, got error: %v", err) } }) t.Run("SuccessWithWaitFlag", func(t *testing.T) { - // Given a temporary directory with mocked dependencies - mocks := setupInstallTest(t) + mocks := setupMocks(t) + + mockBlueprintHandler := blueprint.NewMockBlueprintHandler(mocks.Injector) + mockBlueprintHandler.GenerateFunc = func() *blueprintv1alpha1.Blueprint { + return &blueprintv1alpha1.Blueprint{} + } + mocks.Injector.Register("blueprintHandler", mockBlueprintHandler) + + mockStack := &terraforminfra.MockStack{} + mockStack.InitializeFunc = func() error { return nil } + mocks.Injector.Register("stack", mockStack) + + mockContainerRuntime := &virt.MockVirt{} + mockContainerRuntime.InitializeFunc = func() error { return nil } + mocks.Injector.Register("containerRuntime", mockContainerRuntime) - // When executing the install command with wait flag cmd := createTestInstallCmd() ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector) cmd.SetArgs([]string{"--wait"}) cmd.SetContext(ctx) err := cmd.Execute() - // Then no error should occur if err != nil { t.Errorf("Expected success, got error: %v", err) } }) - t.Run("SuccessWithVerboseContext", func(t *testing.T) { - // Given a temporary directory with mocked dependencies - mocks := setupInstallTest(t) + t.Run("ErrorCheckingTrustedDirectory", func(t *testing.T) { + mocks := setupMocks(t) + + mockShell := mocks.Shell + mockShell.CheckTrustedDirectoryFunc = func() error { + return fmt.Errorf("not in trusted directory") + } - // When executing the install command with verbose context cmd := createTestInstallCmd() ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector) - ctx = context.WithValue(ctx, "verbose", true) - cmd.SetArgs([]string{}) cmd.SetContext(ctx) err := cmd.Execute() - // Then no error should occur - if err != nil { - t.Errorf("Expected success, got error: %v", err) + if err == nil { + t.Error("Expected error, got nil") + } + if !strings.Contains(err.Error(), "not in a trusted directory") { + t.Errorf("Expected trusted directory error, got: %v", err) } }) - // Note: ReturnsErrorWhenEnvPipelineSetupFails test removed - env pipeline no longer used - - t.Run("ReturnsErrorWhenInstallPipelineSetupFails", func(t *testing.T) { - // Given a temporary directory with mocked dependencies - mocks := setupInstallTest(t) + t.Run("ErrorLoadingConfig", func(t *testing.T) { + mockConfigHandler := config.NewMockConfigHandler() + mockConfigHandler.LoadConfigFunc = func() error { + return fmt.Errorf("config load failed") + } - // Override install pipeline to return error during execution - mockInstallPipeline := pipelines.NewMockBasePipeline() - mockInstallPipeline.InitializeFunc = func(injector di.Injector, ctx context.Context) error { return nil } - mockInstallPipeline.ExecuteFunc = func(ctx context.Context) error { - return fmt.Errorf("install pipeline execution failed") + opts := &SetupOptions{ + ConfigHandler: mockConfigHandler, } - mocks.Injector.Register("installPipeline", mockInstallPipeline) + mocks := setupMocks(t, opts) - // When executing the install command cmd := createTestInstallCmd() ctx := context.WithValue(context.Background(), injectorKey, mocks.Injector) - cmd.SetArgs([]string{}) cmd.SetContext(ctx) err := cmd.Execute() - // Then an error should be returned if err == nil { - t.Fatal("Expected error, got nil") + t.Error("Expected error, got nil") } - if !strings.Contains(err.Error(), "Error executing install pipeline") { - t.Errorf("Expected install pipeline execution error, got %q", err.Error()) + if !strings.Contains(err.Error(), "failed to load config") { + t.Errorf("Expected config load error, got: %v", err) } }) } From 69951b27bf29284892fb9fc4304207a0172ede0f Mon Sep 17 00:00:00 2001 From: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> Date: Thu, 13 Nov 2025 20:55:01 -0500 Subject: [PATCH 2/2] Create tmpDir in tests Signed-off-by: Ryan VanGundy <85766511+rmvangun@users.noreply.github.com> --- cmd/root_test.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/root_test.go b/cmd/root_test.go index 4b954d613..52f8b2ee2 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -13,13 +13,13 @@ import ( "testing" "github.com/spf13/cobra" + blueprintpkg "github.com/windsorcli/cli/pkg/composer/blueprint" "github.com/windsorcli/cli/pkg/context/config" - "github.com/windsorcli/cli/pkg/di" envvars "github.com/windsorcli/cli/pkg/context/env" - "github.com/windsorcli/cli/pkg/provisioner/kubernetes" - blueprintpkg "github.com/windsorcli/cli/pkg/composer/blueprint" "github.com/windsorcli/cli/pkg/context/secrets" "github.com/windsorcli/cli/pkg/context/shell" + "github.com/windsorcli/cli/pkg/di" + "github.com/windsorcli/cli/pkg/provisioner/kubernetes" ) // ============================================================================= @@ -34,6 +34,7 @@ type Mocks struct { EnvPrinter *envvars.MockEnvPrinter Shims *Shims BlueprintHandler *blueprintpkg.MockBlueprintHandler + TmpDir string } type SetupOptions struct { @@ -82,6 +83,9 @@ func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks { // Set global shims shims = testShims + // Create temporary directory for test + tmpDir := t.TempDir() + // Create injector var injector di.Injector if options.Injector == nil { @@ -93,7 +97,7 @@ func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks { // Create and register mock shell mockShell := shell.NewMockShell() mockShell.GetProjectRootFunc = func() (string, error) { - return t.TempDir(), nil + return tmpDir, nil } mockShell.CheckTrustedDirectoryFunc = func() error { return nil @@ -146,6 +150,14 @@ func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks { configHandler = config.NewConfigHandler(injector) } else { configHandler = options.ConfigHandler + // If it's a mock config handler, set GetConfigRootFunc to use tmpDir + if mockConfig, ok := configHandler.(*config.MockConfigHandler); ok { + if mockConfig.GetConfigRootFunc == nil { + mockConfig.GetConfigRootFunc = func() (string, error) { + return tmpDir, nil + } + } + } } // Register config handler @@ -188,6 +200,7 @@ func setupMocks(t *testing.T, opts ...*SetupOptions) *Mocks { EnvPrinter: mockEnvPrinter, Shims: testShims, BlueprintHandler: mockBlueprintHandler, + TmpDir: tmpDir, } }