-
Notifications
You must be signed in to change notification settings - Fork 9.5k
/
backend_local.go
124 lines (103 loc) · 3.18 KB
/
backend_local.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package local
import (
"errors"
"fmt"
"log"
"strings"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/go-multierror"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/state"
"github.com/hashicorp/terraform/terraform"
)
// backend.Local implementation.
func (b *Local) Context(op *backend.Operation) (*terraform.Context, state.State, error) {
// Make sure the type is invalid. We use this as a way to know not
// to ask for input/validate.
op.Type = backend.OperationTypeInvalid
return b.context(op)
}
func (b *Local) context(op *backend.Operation) (*terraform.Context, state.State, error) {
// Get the state.
s, err := b.State(op.Workspace)
if err != nil {
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
}
if err := s.RefreshState(); err != nil {
return nil, nil, errwrap.Wrapf("Error loading state: {{err}}", err)
}
// Initialize our context options
var opts terraform.ContextOpts
if v := b.ContextOpts; v != nil {
opts = *v
}
// Copy set options from the operation
opts.Destroy = op.Destroy
opts.Module = op.Module
opts.Targets = op.Targets
opts.UIInput = op.UIIn
if op.Variables != nil {
opts.Variables = op.Variables
}
// Load our state
opts.State = s.State()
// Build the context
var tfCtx *terraform.Context
if op.Plan != nil {
tfCtx, err = op.Plan.Context(&opts)
} else {
tfCtx, err = terraform.NewContext(&opts)
}
// any errors resolving plugins returns this
if rpe, ok := err.(*terraform.ResourceProviderError); ok {
b.pluginInitRequired(rpe)
// we wrote the full UI error here, so return a generic error for flow
// control in the command.
return nil, nil, errors.New("error satisfying plugin requirements")
}
if err != nil {
return nil, nil, err
}
// If we have an operation, then we automatically do the input/validate
// here since every option requires this.
if op.Type != backend.OperationTypeInvalid {
// If input asking is enabled, then do that
if op.Plan == nil && b.OpInput {
mode := terraform.InputModeProvider
mode |= terraform.InputModeVar
mode |= terraform.InputModeVarUnset
if err := tfCtx.Input(mode); err != nil {
return nil, nil, errwrap.Wrapf("Error asking for user input: {{err}}", err)
}
}
// If validation is enabled, validate
if b.OpValidation {
// We ignore warnings here on purpose. We expect users to be listening
// to the terraform.Hook called after a validation.
ws, es := tfCtx.Validate()
if len(ws) > 0 {
// Log just in case the CLI isn't enabled
log.Printf("[WARN] backend/local: %d warnings: %v", len(ws), ws)
// If we have a CLI, output the warnings
if b.CLI != nil {
b.CLI.Warn(strings.TrimSpace(validateWarnHeader) + "\n")
for _, w := range ws {
b.CLI.Warn(fmt.Sprintf(" * %s", w))
}
// Make a newline before continuing
b.CLI.Output("")
}
}
if len(es) > 0 {
return nil, nil, multierror.Append(nil, es...)
}
}
}
return tfCtx, s, nil
}
const validateWarnHeader = `
There are warnings related to your configuration. If no errors occurred,
Terraform will continue despite these warnings. It is a good idea to resolve
these warnings in the near future.
Warnings:
`