147 lines
4.0 KiB
Go
147 lines
4.0 KiB
Go
package views
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/terraform/command/arguments"
|
|
"github.com/hashicorp/terraform/command/format"
|
|
"github.com/hashicorp/terraform/plans"
|
|
"github.com/hashicorp/terraform/states"
|
|
"github.com/hashicorp/terraform/states/statefile"
|
|
"github.com/hashicorp/terraform/terraform"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
)
|
|
|
|
type Operation interface {
|
|
Interrupted()
|
|
FatalInterrupt()
|
|
Stopping()
|
|
Cancelled(destroy bool)
|
|
|
|
EmergencyDumpState(stateFile *statefile.File) error
|
|
|
|
PlanNoChanges()
|
|
Plan(plan *plans.Plan, baseState *states.State, schemas *terraform.Schemas)
|
|
PlanNextStep(planPath string)
|
|
|
|
Diagnostics(diags tfdiags.Diagnostics)
|
|
}
|
|
|
|
func NewOperation(vt arguments.ViewType, inAutomation bool, view *View) Operation {
|
|
switch vt {
|
|
case arguments.ViewHuman:
|
|
return &OperationHuman{view: view, inAutomation: inAutomation}
|
|
default:
|
|
panic(fmt.Sprintf("unknown view type %v", vt))
|
|
}
|
|
}
|
|
|
|
type OperationHuman struct {
|
|
view *View
|
|
|
|
// inAutomation indicates that commands are being run by an
|
|
// automated system rather than directly at a command prompt.
|
|
//
|
|
// This is a hint not to produce messages that expect that a user can
|
|
// run a follow-up command, perhaps because Terraform is running in
|
|
// some sort of workflow automation tool that abstracts away the
|
|
// exact commands that are being run.
|
|
inAutomation bool
|
|
}
|
|
|
|
var _ Operation = (*OperationHuman)(nil)
|
|
|
|
func (v *OperationHuman) Interrupted() {
|
|
v.view.streams.Println(format.WordWrap(interrupted, v.view.outputColumns()))
|
|
}
|
|
|
|
func (v *OperationHuman) FatalInterrupt() {
|
|
v.view.streams.Eprintln(format.WordWrap(fatalInterrupt, v.view.errorColumns()))
|
|
}
|
|
|
|
const fatalInterrupt = `
|
|
Two interrupts received. Exiting immediately. Note that data loss may have occurred.
|
|
`
|
|
|
|
const interrupted = `
|
|
Interrupt received.
|
|
Please wait for Terraform to exit or data loss may occur.
|
|
Gracefully shutting down...
|
|
`
|
|
|
|
func (v *OperationHuman) Stopping() {
|
|
v.view.streams.Println("Stopping operation...")
|
|
}
|
|
|
|
func (v *OperationHuman) Cancelled(destroy bool) {
|
|
if destroy {
|
|
v.view.streams.Println("Destroy cancelled.")
|
|
} else {
|
|
v.view.streams.Println("Apply cancelled.")
|
|
}
|
|
}
|
|
|
|
func (v *OperationHuman) EmergencyDumpState(stateFile *statefile.File) error {
|
|
stateBuf := new(bytes.Buffer)
|
|
jsonErr := statefile.Write(stateFile, stateBuf)
|
|
if jsonErr != nil {
|
|
return jsonErr
|
|
}
|
|
v.view.streams.Eprintln(stateBuf)
|
|
return nil
|
|
}
|
|
|
|
func (v *OperationHuman) PlanNoChanges() {
|
|
v.view.streams.Println("\n" + v.view.colorize.Color(strings.TrimSpace(planNoChanges)))
|
|
v.view.streams.Println("\n" + strings.TrimSpace(format.WordWrap(planNoChangesDetail, v.view.outputColumns())))
|
|
}
|
|
|
|
func (v *OperationHuman) Plan(plan *plans.Plan, baseState *states.State, schemas *terraform.Schemas) {
|
|
renderPlan(plan, baseState, schemas, v.view)
|
|
}
|
|
|
|
// PlanNextStep gives the user some next-steps, unless we're running in an
|
|
// automation tool which is presumed to provide its own UI for further actions.
|
|
func (v *OperationHuman) PlanNextStep(planPath string) {
|
|
if v.inAutomation {
|
|
return
|
|
}
|
|
v.view.outputHorizRule()
|
|
|
|
if planPath == "" {
|
|
v.view.streams.Print(
|
|
"\n" + strings.TrimSpace(format.WordWrap(planHeaderNoOutput, v.view.outputColumns())) + "\n",
|
|
)
|
|
} else {
|
|
v.view.streams.Printf(
|
|
"\n"+strings.TrimSpace(format.WordWrap(planHeaderYesOutput, v.view.outputColumns()))+"\n",
|
|
planPath, planPath,
|
|
)
|
|
}
|
|
}
|
|
|
|
func (v *OperationHuman) Diagnostics(diags tfdiags.Diagnostics) {
|
|
v.view.Diagnostics(diags)
|
|
}
|
|
|
|
const planNoChanges = `
|
|
[reset][bold][green]No changes. Infrastructure is up-to-date.[reset][green]
|
|
`
|
|
|
|
const planNoChangesDetail = `
|
|
That Terraform did not detect any differences between your configuration and the remote system(s). As a result, there are no actions to take.
|
|
`
|
|
|
|
const planHeaderNoOutput = `
|
|
Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
|
|
`
|
|
|
|
const planHeaderYesOutput = `
|
|
Saved the plan to: %s
|
|
|
|
To perform exactly these actions, run the following command to apply:
|
|
terraform apply %q
|
|
`
|