| package cloud |
| |
| import ( |
| "context" |
| "fmt" |
| "strconv" |
| "time" |
| |
| "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) |
| Output(str string) |
| SubOutput(str string) |
| } |
| |
| // IntegrationContext is a set of data that is useful when performing Terraform Cloud integration operations |
| type IntegrationContext struct { |
| B *Cloud |
| StopContext context.Context |
| CancelContext context.Context |
| Op *backend.Operation |
| Run *tfe.Run |
| } |
| |
| // integrationCLIOutput implements IntegrationOutputWriter |
| type integrationCLIOutput struct { |
| CLI cli.Ui |
| Colorizer Colorer |
| started time.Time |
| } |
| |
| var _ IntegrationOutputWriter = (*integrationCLIOutput)(nil) // Compile time check |
| |
| func (s *IntegrationContext) Poll(backoffMinInterval float64, backoffMaxInterval float64, every func(i int) (bool, error)) error { |
| for i := 0; ; i++ { |
| select { |
| case <-s.StopContext.Done(): |
| return s.StopContext.Err() |
| case <-s.CancelContext.Done(): |
| return s.CancelContext.Err() |
| case <-time.After(backoff(backoffMinInterval, backoffMaxInterval, i)): |
| // blocks for a time between min and max |
| } |
| |
| cont, err := every(i) |
| if !cont { |
| return err |
| } |
| } |
| } |
| |
| // 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{ |
| CLI: s.B.CLI, |
| Colorizer: s.B.Colorize(), |
| started: time.Now(), |
| } |
| |
| result.Output("\n[bold]" + name + ":\n") |
| |
| return result |
| } |
| |
| // End writes the termination output for the integration |
| func (s *integrationCLIOutput) End() { |
| if s.CLI == nil { |
| return |
| } |
| |
| 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.CLI == nil { |
| return |
| } |
| 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.CLI == nil { |
| return |
| } |
| 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 ... |
| // 13 tasks still pending, 0 passed, 0 failed ... (8s elapsed) |
| // 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.CLI == nil { |
| return |
| } |
| 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))) |
| } |