-
Notifications
You must be signed in to change notification settings - Fork 29
/
plan.go
292 lines (236 loc) · 10.3 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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tfjson
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/hashicorp/go-version"
)
// PlanFormatVersionConstraints defines the versions of the JSON plan format
// that are supported by this package.
var PlanFormatVersionConstraints = ">= 0.1, < 2.0"
// ResourceMode is a string representation of the resource type found
// in certain fields in the plan.
type ResourceMode string
const (
// DataResourceMode is the resource mode for data sources.
DataResourceMode ResourceMode = "data"
// ManagedResourceMode is the resource mode for managed resources.
ManagedResourceMode ResourceMode = "managed"
)
// Plan represents the entire contents of an output Terraform plan.
type Plan struct {
// useJSONNumber opts into the behavior of calling
// json.Decoder.UseNumber prior to decoding the plan, which turns
// numbers into json.Numbers instead of float64s. Set it using
// Plan.UseJSONNumber.
useJSONNumber bool
// The version of the plan format. This should always match the
// PlanFormatVersion constant in this package, or else an unmarshal
// will be unstable.
FormatVersion string `json:"format_version,omitempty"`
// The version of Terraform used to make the plan.
TerraformVersion string `json:"terraform_version,omitempty"`
// The variables set in the root module when creating the plan.
Variables map[string]*PlanVariable `json:"variables,omitempty"`
// The common state representation of resources within this plan.
// This is a product of the existing state merged with the diff for
// this plan.
PlannedValues *StateValues `json:"planned_values,omitempty"`
// The change operations for resources and data sources within this plan
// resulting from resource drift.
ResourceDrift []*ResourceChange `json:"resource_drift,omitempty"`
// The change operations for resources and data sources within this
// plan.
ResourceChanges []*ResourceChange `json:"resource_changes,omitempty"`
// DeferredChanges contains the change operations for resources that are deferred
// for this plan.
DeferredChanges []*DeferredResourceChange `json:"deferred_changes,omitempty"`
// Complete indicates that all resources have successfully planned changes.
// This will be false if there are DeferredChanges or if the -target flag is used.
//
// Complete was introduced in Terraform 1.8 and will be nil for all previous
// Terraform versions.
Complete *bool `json:"complete,omitempty"`
// The change operations for outputs within this plan.
OutputChanges map[string]*Change `json:"output_changes,omitempty"`
// The Terraform state prior to the plan operation. This is the
// same format as PlannedValues, without the current diff merged.
PriorState *State `json:"prior_state,omitempty"`
// The Terraform configuration used to make the plan.
Config *Config `json:"configuration,omitempty"`
// RelevantAttributes represents any resource instances and their
// attributes which may have contributed to the planned changes
RelevantAttributes []ResourceAttribute `json:"relevant_attributes,omitempty"`
// Checks contains the results of any conditional checks executed, or
// planned to be executed, during this plan.
Checks []CheckResultStatic `json:"checks,omitempty"`
// Timestamp contains the static timestamp that Terraform considers to be
// the time this plan executed, in UTC.
Timestamp string `json:"timestamp,omitempty"`
}
// ResourceAttribute describes a full path to a resource attribute
type ResourceAttribute struct {
// Resource describes resource instance address (e.g. null_resource.foo)
Resource string `json:"resource"`
// Attribute describes the attribute path using a lossy representation
// of cty.Path. (e.g. ["id"] or ["objects", 0, "val"]).
Attribute []json.RawMessage `json:"attribute"`
}
// UseJSONNumber controls whether the Plan will be decoded using the
// json.Number behavior or the float64 behavior. When b is true, the Plan will
// represent numbers in PlanOutputs as json.Numbers. When b is false, the
// Plan will represent numbers in PlanOutputs as float64s.
func (p *Plan) UseJSONNumber(b bool) {
p.useJSONNumber = b
}
// Validate checks to ensure that the plan is present, and the
// version matches the version supported by this library.
func (p *Plan) Validate() error {
if p == nil {
return errors.New("plan is nil")
}
if p.FormatVersion == "" {
return errors.New("unexpected plan input, format version is missing")
}
constraint, err := version.NewConstraint(PlanFormatVersionConstraints)
if err != nil {
return fmt.Errorf("invalid version constraint: %w", err)
}
version, err := version.NewVersion(p.FormatVersion)
if err != nil {
return fmt.Errorf("invalid format version %q: %w", p.FormatVersion, err)
}
if !constraint.Check(version) {
return fmt.Errorf("unsupported plan format version: %q does not satisfy %q",
version, constraint)
}
return nil
}
func isStringInSlice(slice []string, s string) bool {
for _, el := range slice {
if el == s {
return true
}
}
return false
}
func (p *Plan) UnmarshalJSON(b []byte) error {
type rawPlan Plan
var plan rawPlan
dec := json.NewDecoder(bytes.NewReader(b))
if p.useJSONNumber {
dec.UseNumber()
}
err := dec.Decode(&plan)
if err != nil {
return err
}
*p = *(*Plan)(&plan)
return p.Validate()
}
// ResourceChange is a description of an individual change action
// that Terraform plans to use to move from the prior state to a new
// state matching the configuration.
type ResourceChange struct {
// The absolute resource address.
Address string `json:"address,omitempty"`
// The absolute address that this resource instance had
// at the conclusion of a previous plan.
PreviousAddress string `json:"previous_address,omitempty"`
// The module portion of the above address. Omitted if the instance
// is in the root module.
ModuleAddress string `json:"module_address,omitempty"`
// The resource mode.
Mode ResourceMode `json:"mode,omitempty"`
// The resource type, example: "aws_instance" for aws_instance.foo.
Type string `json:"type,omitempty"`
// The resource name, example: "foo" for aws_instance.foo.
Name string `json:"name,omitempty"`
// The instance key for any resources that have been created using
// "count" or "for_each". If neither of these apply the key will be
// empty.
//
// This value can be either an integer (int) or a string.
Index interface{} `json:"index,omitempty"`
// The name of the provider this resource belongs to. This allows
// the provider to be interpreted unambiguously in the unusual
// situation where a provider offers a resource type whose name
// does not start with its own name, such as the "googlebeta"
// provider offering "google_compute_instance".
ProviderName string `json:"provider_name,omitempty"`
// An identifier used during replacement operations, and can be
// used to identify the exact resource being replaced in state.
DeposedKey string `json:"deposed,omitempty"`
// The data describing the change that will be made to this object.
Change *Change `json:"change,omitempty"`
}
// Change is the representation of a proposed change for an object.
type Change struct {
// The action to be carried out by this change.
Actions Actions `json:"actions,omitempty"`
// Before and After are representations of the object value both
// before and after the action. For create and delete actions,
// either Before or After is unset (respectively). For no-op
// actions, both values will be identical. After will be incomplete
// if there are values within it that won't be known until after
// apply.
Before interface{} `json:"before,"`
After interface{} `json:"after,omitempty"`
// A deep object of booleans that denotes any values that are
// unknown in a resource. These values were previously referred to
// as "computed" values.
//
// If the value cannot be found in this map, then its value should
// be available within After, so long as the operation supports it.
AfterUnknown interface{} `json:"after_unknown,omitempty"`
// BeforeSensitive and AfterSensitive are object values with similar
// structure to Before and After, but with all sensitive leaf values
// replaced with true, and all non-sensitive leaf values omitted. These
// objects should be combined with Before and After to prevent accidental
// display of sensitive values in user interfaces.
BeforeSensitive interface{} `json:"before_sensitive,omitempty"`
AfterSensitive interface{} `json:"after_sensitive,omitempty"`
// Importing contains the import metadata about this operation. If importing
// is present (ie. not null) then the change is an import operation in
// addition to anything mentioned in the actions field. The actual contents
// of the Importing struct is subject to change, so downstream consumers
// should treat any values in here as strictly optional.
Importing *Importing `json:"importing,omitempty"`
// GeneratedConfig contains any HCL config generated for this resource
// during planning as a string.
//
// If this is populated, then Importing should also be populated but this
// might change in the future. However, not all Importing changes will
// contain generated config.
GeneratedConfig string `json:"generated_config,omitempty"`
// ReplacePaths contains a set of paths that point to attributes/elements
// that are causing the overall resource to be replaced rather than simply
// updated.
//
// This field is always a slice of indexes, where an index in this context
// is either an integer pointing to a child of a set/list, or a string
// pointing to the child of a map, object, or block.
ReplacePaths []interface{} `json:"replace_paths,omitempty"`
}
// Importing is a nested object for the resource import metadata.
type Importing struct {
// The original ID of this resource used to target it as part of planned
// import operation.
ID string `json:"id,omitempty"`
}
// PlanVariable is a top-level variable in the Terraform plan.
type PlanVariable struct {
// The value for this variable at plan time.
Value interface{} `json:"value,omitempty"`
}
// DeferredResourceChange is a description of a resource change that has been
// deferred for some reason.
type DeferredResourceChange struct {
// Reason is the reason why this resource change was deferred.
Reason string `json:"reason,omitempty"`
// Change contains any information we have about the deferred change.
ResourceChange *ResourceChange `json:"resource_change,omitempty"`
}