backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
package views
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
2021-07-15 19:30:11 +02:00
|
|
|
"sort"
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
"strings"
|
|
|
|
|
2021-05-17 21:00:50 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
2021-05-17 21:07:38 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/command/arguments"
|
|
|
|
"github.com/hashicorp/terraform/internal/command/format"
|
|
|
|
"github.com/hashicorp/terraform/internal/command/views/json"
|
2021-05-17 21:33:17 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/plans"
|
2021-06-29 20:14:31 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/states"
|
2021-05-17 21:43:35 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/states/statefile"
|
2021-05-17 21:46:19 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/terraform"
|
2021-05-17 19:11:06 +02:00
|
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
2021-06-29 20:14:31 +02:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type Operation interface {
|
2021-02-18 23:23:34 +01:00
|
|
|
Interrupted()
|
|
|
|
FatalInterrupt()
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
Stopping()
|
2021-04-06 01:28:59 +02:00
|
|
|
Cancelled(planMode plans.Mode)
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
|
|
|
|
EmergencyDumpState(stateFile *statefile.File) error
|
|
|
|
|
2021-02-23 16:16:09 +01:00
|
|
|
PlannedChange(change *plans.ResourceInstanceChangeSrc)
|
2021-05-06 00:24:58 +02:00
|
|
|
Plan(plan *plans.Plan, schemas *terraform.Schemas)
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
PlanNextStep(planPath string)
|
|
|
|
|
|
|
|
Diagnostics(diags tfdiags.Diagnostics)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewOperation(vt arguments.ViewType, inAutomation bool, view *View) Operation {
|
|
|
|
switch vt {
|
|
|
|
case arguments.ViewHuman:
|
2021-02-26 22:31:12 +01:00
|
|
|
return &OperationHuman{view: view, inAutomation: inAutomation}
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("unknown view type %v", vt))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type OperationHuman struct {
|
2021-02-26 22:31:12 +01:00
|
|
|
view *View
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
2021-02-18 23:23:34 +01:00
|
|
|
func (v *OperationHuman) Interrupted() {
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Println(format.WordWrap(interrupted, v.view.outputColumns()))
|
2021-02-18 23:23:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationHuman) FatalInterrupt() {
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Eprintln(format.WordWrap(fatalInterrupt, v.view.errorColumns()))
|
2021-02-18 23:23:34 +01:00
|
|
|
}
|
|
|
|
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
func (v *OperationHuman) Stopping() {
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Println("Stopping operation...")
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
}
|
|
|
|
|
2021-04-06 01:28:59 +02:00
|
|
|
func (v *OperationHuman) Cancelled(planMode plans.Mode) {
|
|
|
|
switch planMode {
|
|
|
|
case plans.DestroyMode:
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Println("Destroy cancelled.")
|
2021-04-06 01:28:59 +02:00
|
|
|
default:
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Println("Apply cancelled.")
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationHuman) EmergencyDumpState(stateFile *statefile.File) error {
|
|
|
|
stateBuf := new(bytes.Buffer)
|
|
|
|
jsonErr := statefile.Write(stateFile, stateBuf)
|
|
|
|
if jsonErr != nil {
|
|
|
|
return jsonErr
|
|
|
|
}
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Eprintln(stateBuf)
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-05-06 00:24:58 +02:00
|
|
|
func (v *OperationHuman) Plan(plan *plans.Plan, schemas *terraform.Schemas) {
|
|
|
|
renderPlan(plan, schemas, v.view)
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
}
|
|
|
|
|
2021-02-23 16:16:09 +01:00
|
|
|
func (v *OperationHuman) PlannedChange(change *plans.ResourceInstanceChangeSrc) {
|
2021-05-07 00:22:48 +02:00
|
|
|
// PlannedChange is primarily for machine-readable output in order to
|
|
|
|
// get a per-resource-instance change description. We don't use it
|
|
|
|
// with OperationHuman because the output of Plan already includes the
|
|
|
|
// change details for all resource instances.
|
2021-02-23 16:16:09 +01:00
|
|
|
}
|
|
|
|
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
// 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
|
|
|
|
}
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.outputHorizRule()
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
|
|
|
|
if planPath == "" {
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Print(
|
|
|
|
"\n" + strings.TrimSpace(format.WordWrap(planHeaderNoOutput, v.view.outputColumns())) + "\n",
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
)
|
|
|
|
} else {
|
2021-02-26 22:31:12 +01:00
|
|
|
v.view.streams.Printf(
|
|
|
|
"\n"+strings.TrimSpace(format.WordWrap(planHeaderYesOutput, v.view.outputColumns()))+"\n",
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
planPath, planPath,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-26 22:31:12 +01:00
|
|
|
func (v *OperationHuman) Diagnostics(diags tfdiags.Diagnostics) {
|
|
|
|
v.view.Diagnostics(diags)
|
|
|
|
}
|
|
|
|
|
2021-02-23 16:16:09 +01:00
|
|
|
type OperationJSON struct {
|
|
|
|
view *JSONView
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ Operation = (*OperationJSON)(nil)
|
|
|
|
|
|
|
|
func (v *OperationJSON) Interrupted() {
|
|
|
|
v.view.Log(interrupted)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationJSON) FatalInterrupt() {
|
|
|
|
v.view.Log(fatalInterrupt)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationJSON) Stopping() {
|
|
|
|
v.view.Log("Stopping operation...")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationJSON) Cancelled(planMode plans.Mode) {
|
|
|
|
switch planMode {
|
|
|
|
case plans.DestroyMode:
|
|
|
|
v.view.Log("Destroy cancelled")
|
|
|
|
default:
|
|
|
|
v.view.Log("Apply cancelled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationJSON) EmergencyDumpState(stateFile *statefile.File) error {
|
|
|
|
stateBuf := new(bytes.Buffer)
|
|
|
|
jsonErr := statefile.Write(stateFile, stateBuf)
|
|
|
|
if jsonErr != nil {
|
|
|
|
return jsonErr
|
|
|
|
}
|
|
|
|
v.view.StateDump(stateBuf.String())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log a change summary and a series of "planned" messages for the changes in
|
|
|
|
// the plan.
|
2021-05-06 00:24:58 +02:00
|
|
|
func (v *OperationJSON) Plan(plan *plans.Plan, schemas *terraform.Schemas) {
|
2021-06-29 20:14:31 +02:00
|
|
|
if err := v.resourceDrift(plan.PrevRunState, plan.PriorState, schemas); err != nil {
|
|
|
|
var diags tfdiags.Diagnostics
|
|
|
|
diags = diags.Append(err)
|
|
|
|
v.Diagnostics(diags)
|
|
|
|
}
|
|
|
|
|
2021-02-23 16:16:09 +01:00
|
|
|
cs := &json.ChangeSummary{
|
|
|
|
Operation: json.OperationPlanned,
|
|
|
|
}
|
|
|
|
for _, change := range plan.Changes.Resources {
|
|
|
|
if change.Action == plans.Delete && change.Addr.Resource.Resource.Mode == addrs.DataResourceMode {
|
|
|
|
// Avoid rendering data sources on deletion
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch change.Action {
|
|
|
|
case plans.Create:
|
|
|
|
cs.Add++
|
|
|
|
case plans.Delete:
|
|
|
|
cs.Remove++
|
|
|
|
case plans.Update:
|
|
|
|
cs.Change++
|
|
|
|
case plans.CreateThenDelete, plans.DeleteThenCreate:
|
|
|
|
cs.Add++
|
|
|
|
cs.Remove++
|
|
|
|
}
|
|
|
|
|
|
|
|
if change.Action != plans.NoOp {
|
|
|
|
v.view.PlannedChange(json.NewResourceInstanceChange(change))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v.view.ChangeSummary(cs)
|
|
|
|
}
|
|
|
|
|
2021-06-29 20:14:31 +02:00
|
|
|
func (v *OperationJSON) resourceDrift(oldState, newState *states.State, schemas *terraform.Schemas) error {
|
|
|
|
if newState.ManagedResourcesEqual(oldState) {
|
|
|
|
// Nothing to do, because we only detect and report drift for managed
|
|
|
|
// resource instances.
|
|
|
|
return nil
|
|
|
|
}
|
2021-07-15 19:30:11 +02:00
|
|
|
var changes []*json.ResourceInstanceChange
|
2021-06-29 20:14:31 +02:00
|
|
|
for _, ms := range oldState.Modules {
|
|
|
|
for _, rs := range ms.Resources {
|
|
|
|
if rs.Addr.Resource.Mode != addrs.ManagedResourceMode {
|
|
|
|
// Drift reporting is only for managed resources
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
provider := rs.ProviderConfig.Provider
|
|
|
|
for key, oldIS := range rs.Instances {
|
|
|
|
if oldIS.Current == nil {
|
|
|
|
// Not interested in instances that only have deposed objects
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
addr := rs.Addr.Instance(key)
|
|
|
|
newIS := newState.ResourceInstance(addr)
|
|
|
|
|
|
|
|
schema, _ := schemas.ResourceTypeConfig(
|
|
|
|
provider,
|
|
|
|
addr.Resource.Resource.Mode,
|
|
|
|
addr.Resource.Resource.Type,
|
|
|
|
)
|
|
|
|
if schema == nil {
|
|
|
|
return fmt.Errorf("no schema found for %s (in provider %s)", addr, provider)
|
|
|
|
}
|
|
|
|
ty := schema.ImpliedType()
|
|
|
|
|
|
|
|
oldObj, err := oldIS.Current.Decode(ty)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to decode previous run data for %s: %s", addr, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var newObj *states.ResourceInstanceObject
|
|
|
|
if newIS != nil && newIS.Current != nil {
|
|
|
|
newObj, err = newIS.Current.Decode(ty)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to decode refreshed data for %s: %s", addr, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var oldVal, newVal cty.Value
|
|
|
|
oldVal = oldObj.Value
|
|
|
|
if newObj != nil {
|
|
|
|
newVal = newObj.Value
|
|
|
|
} else {
|
|
|
|
newVal = cty.NullVal(ty)
|
|
|
|
}
|
|
|
|
|
|
|
|
if oldVal.RawEquals(newVal) {
|
|
|
|
// No drift if the two values are semantically equivalent
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can only detect updates and deletes as drift.
|
|
|
|
action := plans.Update
|
|
|
|
if newVal.IsNull() {
|
|
|
|
action = plans.Delete
|
|
|
|
}
|
|
|
|
|
|
|
|
change := &plans.ResourceInstanceChangeSrc{
|
|
|
|
Addr: addr,
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: action,
|
|
|
|
},
|
|
|
|
}
|
2021-07-15 19:30:11 +02:00
|
|
|
changes = append(changes, json.NewResourceInstanceChange(change))
|
2021-06-29 20:14:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-15 19:30:11 +02:00
|
|
|
|
|
|
|
// Sort the change structs lexically by address to give stable output
|
|
|
|
sort.Slice(changes, func(i, j int) bool { return changes[i].Resource.Addr < changes[j].Resource.Addr })
|
|
|
|
|
|
|
|
for _, change := range changes {
|
|
|
|
v.view.ResourceDrift(change)
|
|
|
|
}
|
|
|
|
|
2021-06-29 20:14:31 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-02-23 16:16:09 +01:00
|
|
|
func (v *OperationJSON) PlannedChange(change *plans.ResourceInstanceChangeSrc) {
|
|
|
|
if change.Action == plans.Delete && change.Addr.Resource.Resource.Mode == addrs.DataResourceMode {
|
|
|
|
// Avoid rendering data sources on deletion
|
|
|
|
return
|
|
|
|
}
|
|
|
|
v.view.PlannedChange(json.NewResourceInstanceChange(change))
|
|
|
|
}
|
|
|
|
|
|
|
|
// PlanNextStep does nothing for the JSON view as it is a hook for user-facing
|
|
|
|
// output only applicable to human-readable UI.
|
|
|
|
func (v *OperationJSON) PlanNextStep(planPath string) {
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *OperationJSON) Diagnostics(diags tfdiags.Diagnostics) {
|
|
|
|
v.view.Diagnostics(diags)
|
|
|
|
}
|
|
|
|
|
|
|
|
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...
|
|
|
|
`
|
|
|
|
|
backend/local: Replace CLI with view instance
This commit extracts the remaining UI logic from the local backend,
and removes access to the direct CLI output. This is replaced with an
instance of a `views.Operation` interface, which codifies the current
requirements for the local backend to interact with the user.
The exception to this at present is interactivity: approving a plan
still depends on the `UIIn` field for the backend. This is out of scope
for this commit and can be revisited separately, at which time the
`UIOut` field can also be removed.
Changes in support of this:
- Some instances of direct error output have been replaced with
diagnostics, most notably in the emergency state backup handler. This
requires reformatting the error messages to allow the diagnostic
renderer to line-wrap them;
- The "in-automation" logic has moved out of the backend and into the
view implementation;
- The plan, apply, refresh, and import commands instantiate a view and
set it on the `backend.Operation` struct, as these are the only code
paths which call the `local.Operation()` method that requires it;
- The show command requires the plan rendering code which is now in the
views package, so there is a stub implementation of a `views.Show`
interface there.
Other refactoring work in support of migrating these commands to the
common views code structure will come in follow-up PRs, at which point
we will be able to remove the UI instances from the unit tests for those
commands.
2021-02-17 19:01:30 +01:00
|
|
|
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
|
|
|
|
`
|