core: Encode Targets in saved Planfile
When a user specifies `-target`s on a `terraform plan` and stores the resulting diff in a plan file using `-out` - it usually works just fine since the diff is scoped based on the targets. When there are tainted resources in the state, however, graph nodes to destroy them were popping back into the plan when it was being loaded from a file. This was because Targets weren't being stored in the Planfile, so Terraform didn't know to filter them out. (In the non-Planfile scenario, we still had the Targets loaded directly from the flags.) By encoding Targets in with the Planfile we can ensure that the same filters are always applied. Backwards compatibility should be fine here, since we're just adding a field. The gob encoder/decoder will just do the right thing (ignore/skip the field) with planfiles stored w/ versions that don't know about Targets. Fixes #5183
This commit is contained in:
parent
5d9637ab1a
commit
f882dd1427
|
@ -316,9 +316,10 @@ func (c *Context) Plan() (*Plan, error) {
|
|||
defer c.releaseRun(v)
|
||||
|
||||
p := &Plan{
|
||||
Module: c.module,
|
||||
Vars: c.variables,
|
||||
State: c.state,
|
||||
Module: c.module,
|
||||
Vars: c.variables,
|
||||
State: c.state,
|
||||
Targets: c.targets,
|
||||
}
|
||||
|
||||
var operation walkOperation
|
||||
|
|
|
@ -4010,7 +4010,76 @@ template_file.parent:
|
|||
ID = foo
|
||||
template = Hi
|
||||
type = template_file
|
||||
`)
|
||||
`)
|
||||
if actual != expected {
|
||||
t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Apply_targetedWithTaintedInState(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
p.DiffFn = testDiffFn
|
||||
p.ApplyFn = testApplyFn
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: testModule(t, "apply-tainted-targets"),
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
Targets: []string{"aws_instance.iambeingadded"},
|
||||
State: &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.ifailedprovisioners": &ResourceState{
|
||||
Tainted: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "ifailedprovisioners",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
plan, err := ctx.Plan()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
// Write / Read plan to simulate running it through a Plan file
|
||||
var buf bytes.Buffer
|
||||
if err := WritePlan(plan, &buf); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
planFromFile, err := ReadPlan(&buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
ctx = planFromFile.Context(&ContextOpts{
|
||||
Module: testModule(t, "apply-tainted-targets"),
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
state, err := ctx.Apply()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(state.String())
|
||||
expected := strings.TrimSpace(`
|
||||
aws_instance.iambeingadded:
|
||||
ID = foo
|
||||
aws_instance.ifailedprovisioners: (1 tainted)
|
||||
ID = <not created>
|
||||
Tainted ID 1 = ifailedprovisioners
|
||||
`)
|
||||
if actual != expected {
|
||||
t.Fatalf("expected state: \n%s\ngot: \n%s", expected, actual)
|
||||
}
|
||||
|
|
|
@ -21,10 +21,11 @@ func init() {
|
|||
// Plan represents a single Terraform execution plan, which contains
|
||||
// all the information necessary to make an infrastructure change.
|
||||
type Plan struct {
|
||||
Diff *Diff
|
||||
Module *module.Tree
|
||||
State *State
|
||||
Vars map[string]string
|
||||
Diff *Diff
|
||||
Module *module.Tree
|
||||
State *State
|
||||
Vars map[string]string
|
||||
Targets []string
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
@ -38,6 +39,7 @@ func (p *Plan) Context(opts *ContextOpts) *Context {
|
|||
opts.Module = p.Module
|
||||
opts.State = p.State
|
||||
opts.Variables = p.Vars
|
||||
opts.Targets = p.Targets
|
||||
return NewContext(opts)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
resource "aws_instance" "ifailedprovisioners" { }
|
||||
|
||||
resource "aws_instance" "iambeingadded" { }
|
|
@ -0,0 +1,5 @@
|
|||
resource "aws_instance" "ifailedprovisioners" {
|
||||
}
|
||||
|
||||
resource "aws_instance" "iambeingadded" {
|
||||
}
|
Loading…
Reference in New Issue