forked from hashicorp/terraform
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plan.go
229 lines (188 loc) · 6.86 KB
/
plan.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
package command
import (
"fmt"
"log"
"os"
"strings"
"github.com/hashicorp/terraform/terraform"
)
// PlanCommand is a Command implementation that compares a Terraform
// configuration to an actual infrastructure and shows the differences.
type PlanCommand struct {
Meta
}
func (c *PlanCommand) Run(args []string) int {
var destroy, refresh, detailed bool
var outPath string
var moduleDepth int
args = c.Meta.process(args, true)
cmdFlags := c.Meta.flagSet("plan")
cmdFlags.BoolVar(&destroy, "destroy", false, "destroy")
cmdFlags.BoolVar(&refresh, "refresh", true, "refresh")
c.addModuleDepthFlag(cmdFlags, &moduleDepth)
cmdFlags.StringVar(&outPath, "out", "", "path")
cmdFlags.IntVar(
&c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism")
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
cmdFlags.BoolVar(&detailed, "detailed-exitcode", false, "detailed-exitcode")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
}
var path string
args = cmdFlags.Args()
if len(args) > 1 {
c.Ui.Error(
"The plan command expects at most one argument with the path\n" +
"to a Terraform configuration.\n")
cmdFlags.Usage()
return 1
} else if len(args) == 1 {
path = args[0]
} else {
var err error
path, err = os.Getwd()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
}
}
countHook := new(CountHook)
c.Meta.extraHooks = []terraform.Hook{countHook}
ctx, _, err := c.Context(contextOpts{
Destroy: destroy,
Path: path,
StatePath: c.Meta.statePath,
Parallelism: c.Meta.parallelism,
})
if err != nil {
c.Ui.Error(err.Error())
return 1
}
if err := ctx.Input(c.InputMode()); err != nil {
c.Ui.Error(fmt.Sprintf("Error configuring: %s", err))
return 1
}
if !validateContext(ctx, c.Ui) {
return 1
}
if refresh {
c.Ui.Output("Refreshing Terraform state in-memory prior to plan...")
c.Ui.Output("The refreshed state will be used to calculate this plan, but")
c.Ui.Output("will not be persisted to local or remote state storage.\n")
_, err := ctx.Refresh()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error refreshing state: %s", err))
return 1
}
c.Ui.Output("")
}
plan, err := ctx.Plan()
if err != nil {
c.Ui.Error(fmt.Sprintf("Error running plan: %s", err))
return 1
}
if outPath != "" {
log.Printf("[INFO] Writing plan output to: %s", outPath)
f, err := os.Create(outPath)
if err == nil {
defer f.Close()
err = terraform.WritePlan(plan, f)
}
if err != nil {
c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err))
return 1
}
}
if plan.Diff.Empty() {
c.Ui.Output(
"No changes. Infrastructure is up-to-date. This means that Terraform\n" +
"could not detect any differences between your configuration and\n" +
"the real physical resources that exist. As a result, Terraform\n" +
"doesn't need to do anything.")
return 0
}
if outPath == "" {
c.Ui.Output(strings.TrimSpace(planHeaderNoOutput) + "\n")
} else {
c.Ui.Output(fmt.Sprintf(
strings.TrimSpace(planHeaderYesOutput)+"\n",
outPath))
}
c.Ui.Output(FormatPlan(&FormatPlanOpts{
Plan: plan,
Color: c.Colorize(),
ModuleDepth: moduleDepth,
}))
c.Ui.Output(c.Colorize().Color(fmt.Sprintf(
"[reset][bold]Plan:[reset] "+
"%d to add, %d to change, %d to destroy.",
countHook.ToAdd+countHook.ToRemoveAndAdd,
countHook.ToChange,
countHook.ToRemove+countHook.ToRemoveAndAdd)))
if detailed {
return 2
}
return 0
}
func (c *PlanCommand) Help() string {
helpText := `
Usage: terraform plan [options] [dir]
Generates an execution plan for Terraform.
This execution plan can be reviewed prior to running apply to get a
sense for what Terraform will do. Optionally, the plan can be saved to
a Terraform plan file, and apply can take this plan file to execute
this plan exactly.
Options:
-destroy If set, a plan will be generated to destroy all resources
managed by the given configuration and state.
-detailed-exitcode Return detailed exit codes when the command exits. This
will change the meaning of exit codes to:
0 - Succeeded, diff is empty (no changes)
1 - Errored
2 - Succeeded, there is a diff
-input=true Ask for input for variables if not directly set.
-module-depth=n Specifies the depth of modules to show in the output.
This does not affect the plan itself, only the output
shown. By default, this is -1, which will expand all.
-no-color If specified, output won't contain any color.
-out=path Write a plan file to the given path. This can be used as
input to the "apply" command.
-parallelism=n Limit the number of concurrent operations. Defaults to 10.
-refresh=true Update state prior to checking for differences.
-state=statefile Path to a Terraform state file to use to look
up Terraform-managed resources. By default it will
use the state "terraform.tfstate" if it exists.
-target=resource Resource to target. Operation will be limited to this
resource and its dependencies. This flag can be used
multiple times.
-var 'foo=bar' Set a variable in the Terraform configuration. This
flag can be set multiple times.
-var-file=foo Set variables in the Terraform configuration from
a file. If "terraform.tfvars" is present, it will be
automatically loaded if this flag is not specified.
`
return strings.TrimSpace(helpText)
}
func (c *PlanCommand) Synopsis() string {
return "Generate and show an execution plan"
}
const planHeaderNoOutput = `
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.
`
const planHeaderYesOutput = `
The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.
Your plan was also saved to the path below. Call the "apply" subcommand
with this plan file and Terraform will exactly execute this execution
plan.
Path: %s
`