diff --git a/internal/cloud/backend_plan.go b/internal/cloud/backend_plan.go index 658b0191c..1d6099378 100644 --- a/internal/cloud/backend_plan.go +++ b/internal/cloud/backend_plan.go @@ -355,7 +355,13 @@ in order to capture the filesystem context the remote workspace expects: // Await pre-apply run tasks if len(r.TaskStage) > 0 { - context := NewIntegrationContext(stopCtx, cancelCtx, b, op, r) + context := &IntegrationContext{ + B: b, + StopContext: stopCtx, + CancelContext: cancelCtx, + Op: op, + Run: r, + } if stageID := getTaskStageIDByName(r.TaskStage, "pre_apply"); stageID != nil { err = b.runTasks(context, context.BeginOutput("Run Tasks (pre-apply)"), *stageID) diff --git a/internal/cloud/backend_runTasks.go b/internal/cloud/backend_runTasks.go index 6c4d4a270..6b5b0a812 100644 --- a/internal/cloud/backend_runTasks.go +++ b/internal/cloud/backend_runTasks.go @@ -49,7 +49,6 @@ func summarizeTaskResults(taskResults []*tfe.TaskResult) *taskResultSummary { func (b *Cloud) runTasksWithTaskResults(context *IntegrationContext, output IntegrationOutputWriter, fetchTaskStage taskStageReadFunc) error { return context.Poll(func(i int) (bool, error) { - // TODO: get the stage that corresponds to an argument passed to this function stage, err := fetchTaskStage(b, context.StopContext) if err != nil { diff --git a/internal/cloud/backend_runTasks_test.go b/internal/cloud/backend_runTasks_test.go index d33220446..0e0bddc20 100644 --- a/internal/cloud/backend_runTasks_test.go +++ b/internal/cloud/backend_runTasks_test.go @@ -132,7 +132,7 @@ func TestCloud_runTasksWithTaskResults(t *testing.T) { for caseName, c := range cases { c.writer.output.Reset() - err := b.runTasksWithTaskResults(c.writer.ctx, c.writer, func(b *Cloud, stopCtx context.Context) (*tfe.TaskStage, error) { + err := b.runTasksWithTaskResults(c.context, writer, func(b *Cloud, stopCtx context.Context) (*tfe.TaskStage, error) { return &tfe.TaskStage{ TaskResults: c.taskResults, }, nil diff --git a/internal/cloud/cloud_integration.go b/internal/cloud/cloud_integration.go index 168e1ae26..047fc5709 100644 --- a/internal/cloud/cloud_integration.go +++ b/internal/cloud/cloud_integration.go @@ -8,8 +8,11 @@ import ( "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/internal/backend" + "github.com/mitchellh/cli" ) +// IntegrationOutputWriter is an interface used to to write output tailored for +// Terraform Cloud integrations type IntegrationOutputWriter interface { End() OutputElapsed(message string, maxMessage int) @@ -17,8 +20,8 @@ type IntegrationOutputWriter interface { SubOutput(str string) } +// IntegrationContext is a set of data that is useful when performing Terraform Cloud integration operations type IntegrationContext struct { - started time.Time B *Cloud StopContext context.Context CancelContext context.Context @@ -26,22 +29,15 @@ type IntegrationContext struct { Run *tfe.Run } +// integrationCLIOutput implements IntegrationOutputWriter type integrationCLIOutput struct { - ctx *IntegrationContext + CLI cli.Ui + Colorizer Colorer + started time.Time } var _ IntegrationOutputWriter = (*integrationCLIOutput)(nil) // Compile time check -func NewIntegrationContext(stopCtx, cancelCtx context.Context, b *Cloud, op *backend.Operation, r *tfe.Run) *IntegrationContext { - return &IntegrationContext{ - B: b, - StopContext: stopCtx, - CancelContext: cancelCtx, - Op: op, - Run: r, - } -} - func (s *IntegrationContext) Poll(every func(i int) (bool, error)) error { for i := 0; ; i++ { select { @@ -60,48 +56,47 @@ func (s *IntegrationContext) Poll(every func(i int) (bool, error)) error { } } +// BeginOutput writes a preamble to the CLI and creates a new IntegrationOutputWriter interface +// to write the remaining CLI output to. Use IntegrationOutputWriter.End() to complete integration +// output func (s *IntegrationContext) BeginOutput(name string) IntegrationOutputWriter { var result IntegrationOutputWriter = &integrationCLIOutput{ - ctx: s, + CLI: s.B.CLI, + Colorizer: s.B.Colorize(), + started: time.Now(), } - s.started = time.Now() - - if s.HasCLI() { - s.B.CLI.Output("\n------------------------------------------------------------------------\n") - } - - result.Output("[bold]" + name + ":\n") + result.Output("\n[bold]" + name + ":\n") return result } -func (s *IntegrationContext) HasCLI() bool { - return s.B.CLI != nil -} - +// End writes the termination output for the integration func (s *integrationCLIOutput) End() { - if !s.ctx.HasCLI() { + if s.CLI == nil { return } - s.ctx.B.CLI.Output("\n------------------------------------------------------------------------\n") + s.CLI.Output("\n------------------------------------------------------------------------\n") } +// Output writes a string after colorizing it using any [colorstrings](https://github.com/mitchellh/colorstring) it contains func (s *integrationCLIOutput) Output(str string) { - if !s.ctx.HasCLI() { + if s.CLI == nil { return } - s.ctx.B.CLI.Output(s.ctx.B.Colorize().Color(str)) + s.CLI.Output(s.Colorizer.Color(str)) } +// SubOutput writes a string prefixed by a "│ " after colorizing it using any [colorstrings](https://github.com/mitchellh/colorstring) it contains func (s *integrationCLIOutput) SubOutput(str string) { - if !s.ctx.HasCLI() { + if s.CLI == nil { return } - s.ctx.B.CLI.Output(s.ctx.B.Colorize().Color(fmt.Sprintf("[reset]│ %s", str))) + s.CLI.Output(s.Colorizer.Color(fmt.Sprintf("[reset]│ %s", str))) } +// OutputElapsed writes a string followed by the amount of time that has elapsed since calling BeginOutput. // Example pending output; the variable spacing (50 chars) allows up to 99 tasks (two digits) in each category: // --------------- // 13 tasks still pending, 0 passed, 0 failed ... @@ -109,9 +104,9 @@ func (s *integrationCLIOutput) SubOutput(str string) { // 13 tasks still pending, 0 passed, 0 failed ... (19s elapsed) // 13 tasks still pending, 0 passed, 0 failed ... (33s elapsed) func (s *integrationCLIOutput) OutputElapsed(message string, maxMessage int) { - if !s.ctx.HasCLI() { + if s.CLI == nil { return } - elapsed := time.Since(s.ctx.started).Truncate(1 * time.Second) - s.ctx.B.CLI.Output(fmt.Sprintf("%-"+strconv.FormatInt(int64(maxMessage), 10)+"s", message) + s.ctx.B.Colorize().Color(fmt.Sprintf("[dim](%s elapsed)", elapsed))) + elapsed := time.Since(s.started).Truncate(1 * time.Second) + s.CLI.Output(fmt.Sprintf("%-"+strconv.FormatInt(int64(maxMessage), 10)+"s", message) + s.Colorizer.Color(fmt.Sprintf("[dim](%s elapsed)", elapsed))) }