-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
apply.go
147 lines (125 loc) · 4.7 KB
/
apply.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package arguments
import (
"fmt"
"github.com/hashicorp/terraform/internal/plans"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// Apply represents the command-line arguments for the apply command.
type Apply struct {
// State, Operation, and Vars are the common extended flags
State *State
Operation *Operation
Vars *Vars
// AutoApprove skips the manual verification step for the apply operation.
AutoApprove bool
// InputEnabled is used to disable interactive input for unspecified
// variable and backend config values. Default is true.
InputEnabled bool
// PlanPath contains an optional path to a stored plan file
PlanPath string
// ViewType specifies which output format to use
ViewType ViewType
}
// ParseApply processes CLI arguments, returning an Apply value and errors.
// If errors are encountered, an Apply value is still returned representing
// the best effort interpretation of the arguments.
func ParseApply(args []string) (*Apply, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
apply := &Apply{
State: &State{},
Operation: &Operation{},
Vars: &Vars{},
}
cmdFlags := extendedFlagSet("apply", apply.State, apply.Operation, apply.Vars)
cmdFlags.BoolVar(&apply.AutoApprove, "auto-approve", false, "auto-approve")
cmdFlags.BoolVar(&apply.InputEnabled, "input", true, "input")
var json bool
cmdFlags.BoolVar(&json, "json", false, "json")
if err := cmdFlags.Parse(args); err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to parse command-line flags",
err.Error(),
))
}
args = cmdFlags.Args()
if len(args) > 0 {
apply.PlanPath = args[0]
args = args[1:]
}
if len(args) > 0 {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Too many command line arguments",
"Expected at most one positional argument.",
))
}
// JSON view currently does not support input, so we disable it here.
if json {
apply.InputEnabled = false
}
// JSON view cannot confirm apply, so we require either a plan file or
// auto-approve to be specified. We intentionally fail here rather than
// override auto-approve, which would be dangerous.
if json && apply.PlanPath == "" && !apply.AutoApprove {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Plan file or auto-approve required",
"Terraform cannot ask for interactive approval when -json is set. You can either apply a saved plan file, or enable the -auto-approve option.",
))
}
diags = diags.Append(apply.Operation.Parse())
switch {
case json:
apply.ViewType = ViewJSON
default:
apply.ViewType = ViewHuman
}
return apply, diags
}
// ParseApplyDestroy is a special case of ParseApply that deals with the
// "terraform destroy" command, which is effectively an alias for
// "terraform apply -destroy".
func ParseApplyDestroy(args []string) (*Apply, tfdiags.Diagnostics) {
apply, diags := ParseApply(args)
// So far ParseApply was using the command line options like -destroy
// and -refresh-only to determine the plan mode. For "terraform destroy"
// we expect neither of those arguments to be set, and so the plan mode
// should currently be set to NormalMode, which we'll replace with
// DestroyMode here. If it's already set to something else then that
// suggests incorrect usage.
switch apply.Operation.PlanMode {
case plans.NormalMode:
// This indicates that the user didn't specify any mode options at
// all, which is correct, although we know from the command that
// they actually intended to use DestroyMode here.
apply.Operation.PlanMode = plans.DestroyMode
case plans.DestroyMode:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid mode option",
"The -destroy option is not valid for \"terraform destroy\", because this command always runs in destroy mode.",
))
case plans.RefreshOnlyMode:
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid mode option",
"The -refresh-only option is not valid for \"terraform destroy\".",
))
default:
// This is a non-ideal error message for if we forget to handle a
// newly-handled plan mode in Operation.Parse. Ideally they should all
// have cases above so we can produce better error messages.
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Invalid mode option",
fmt.Sprintf("The \"terraform destroy\" command doesn't support %s.", apply.Operation.PlanMode),
))
}
// NOTE: It's also invalid to have apply.PlanPath set in this codepath,
// but we don't check that in here because we'll return a different error
// message depending on whether the given path seems to refer to a saved
// plan file or to a configuration directory. The apply command
// implementation itself therefore handles this situation.
return apply, diags
}