diff --git a/controller/workflows/infra.go b/controller/workflows/infra.go index 4e11fff..6238abf 100644 --- a/controller/workflows/infra.go +++ b/controller/workflows/infra.go @@ -43,24 +43,30 @@ func Infra(ctx workflow.Context, input InfraInputs) (*activities.Graph, error) { }) var graph *activities.Graph - var changedModules []string - - graphFuture := workflow.ExecuteActivity(analysisCtx, activities.TerragruntGraph, path+"/infra/"+input.Stack) - changedFuture := workflow.ExecuteActivity(analysisCtx, activities.ChangedModules, path, input.OldRevision) + var prunedGraph *activities.Graph - if err := graphFuture.Get(ctx, &graph); err != nil { - return nil, err - } - if err := changedFuture.Get(ctx, &changedModules); err != nil { + // Get the terragrunt graph + if err := workflow.ExecuteActivity(analysisCtx, activities.TerragruntGraph, path+"/infra/"+input.Stack).Get(ctx, &graph); err != nil { return nil, err } - var prunedGraph *activities.Graph - if err := workflow.ExecuteActivity(analysisCtx, activities.PruneGraph, graph, changedModules).Get(ctx, &prunedGraph); err != nil { - return nil, err - } + // If oldRevision is not provided, use the full graph (no pruning) + if input.OldRevision == "" { + logger.Info("No oldRevision provided, using full graph", "nodes", len(graph.Nodes)) + prunedGraph = graph + } else { + // Determine changed modules and prune graph + var changedModules []string + if err := workflow.ExecuteActivity(analysisCtx, activities.ChangedModules, path, input.OldRevision).Get(ctx, &changedModules); err != nil { + return nil, err + } - logger.Info("Graph pruning completed", "nodes", len(prunedGraph.Nodes)) + if err := workflow.ExecuteActivity(analysisCtx, activities.PruneGraph, graph, changedModules).Get(ctx, &prunedGraph); err != nil { + return nil, err + } + + logger.Info("Graph pruning completed", "nodes", len(prunedGraph.Nodes)) + } for levelIndex, level := range prunedGraph.TopologicalSort() { logger.Info("Starting terragrunt apply", "level", levelIndex, "modules", level) diff --git a/controller/workflows/infra_test.go b/controller/workflows/infra_test.go index 1696c0e..ca2a0cb 100644 --- a/controller/workflows/infra_test.go +++ b/controller/workflows/infra_test.go @@ -396,6 +396,70 @@ func (s *InfraWorkflowTestSuite) TestInfraWorkflow_WorkerFailureRetry() { s.True(result.Nodes["module1"]) } +func (s *InfraWorkflowTestSuite) TestInfraWorkflow_NoOldRevisionProvided() { + // Test that when oldRevision is not provided, all modules are deployed + input := InfraInputs{ + Url: "https://github.com/example/repo.git", + Revision: "main", + OldRevision: "", // Empty string - no old revision provided + Stack: "dev", + } + repoPath := "/tmp/infra-12345" + + // Full graph with all modules + graph := &activities.Graph{ + Nodes: map[string]bool{ + "vpc": true, + "database": true, + "loadbalancer": true, + "app": true, + "monitoring": true, + }, + Edges: map[string][]string{ + "app": {"database", "loadbalancer"}, + "database": {"vpc"}, + "loadbalancer": {"vpc"}, + "monitoring": {"app"}, + }, + } + + // When oldRevision is empty, the workflow should use the full graph + // without calling ChangedModules or PruneGraph activities + + s.env.OnActivity(activities.Clone, mock.Anything, input.Url, input.Revision).Return(repoPath, nil) + s.env.OnActivity(activities.TerragruntGraph, mock.Anything, repoPath+"/infra/"+input.Stack).Return(graph, nil) + + // Mock TerragruntApply calls in dependency order for all modules + // Level 0: vpc + s.env.OnActivity(activities.TerragruntApply, mock.Anything, input.Url, input.Revision, "vpc", input.Stack).Return(nil) + // Level 1: database, loadbalancer + s.env.OnActivity(activities.TerragruntApply, mock.Anything, input.Url, input.Revision, "database", input.Stack).Return(nil) + s.env.OnActivity(activities.TerragruntApply, mock.Anything, input.Url, input.Revision, "loadbalancer", input.Stack).Return(nil) + // Level 2: app + s.env.OnActivity(activities.TerragruntApply, mock.Anything, input.Url, input.Revision, "app", input.Stack).Return(nil) + // Level 3: monitoring + s.env.OnActivity(activities.TerragruntApply, mock.Anything, input.Url, input.Revision, "monitoring", input.Stack).Return(nil) + + s.env.ExecuteWorkflow(Infra, input) + + s.True(s.env.IsWorkflowCompleted()) + s.NoError(s.env.GetWorkflowError()) + + var result *activities.Graph + s.NoError(s.env.GetWorkflowResult(&result)) + + // Verify that all modules are in the result graph + s.True(result.Nodes["vpc"]) + s.True(result.Nodes["database"]) + s.True(result.Nodes["loadbalancer"]) + s.True(result.Nodes["app"]) + s.True(result.Nodes["monitoring"]) + + // Verify that the result graph has the same structure as the original + s.Equal(len(graph.Nodes), len(result.Nodes)) + s.Equal(len(graph.Edges), len(result.Edges)) +} + func TestInfraWorkflowTestSuite(t *testing.T) { suite.Run(t, new(InfraWorkflowTestSuite)) }