core: fix targeting in destroy w/ provisioners
The `TargetTransform` was dropping provisioner nodes, which caused graph validation to fail with messages about uninitialized provisioners when a `terraform destroy` was attempted. This was because `destroy` flops the dependency calculation to try and address any nodes in the graph that "depend on" the target node. But we still need to keep the provisioner node in the graph. Here we switch the strategy for filtering nodes to only drop addressable, non-targeted nodes. This should prevent us from having to whitelist nodes to keep in the future. closes #1541
This commit is contained in:
parent
4ef208d81b
commit
5e67657325
|
@ -2688,6 +2688,48 @@ func TestContext2Validate_tainted(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_targetedDestroy(t *testing.T) {
|
||||
m := testModule(t, "validate-targeted")
|
||||
p := testProvider("aws")
|
||||
pr := testProvisioner()
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
},
|
||||
Provisioners: map[string]ResourceProvisionerFactory{
|
||||
"shell": testProvisionerFuncFixed(pr),
|
||||
},
|
||||
State: &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo": resourceState("aws_instance", "i-bcd345"),
|
||||
"aws_instance.bar": resourceState("aws_instance", "i-abc123"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Targets: []string{"aws_instance.foo"},
|
||||
Destroy: true,
|
||||
})
|
||||
|
||||
w, e := ctx.Validate()
|
||||
if len(w) > 0 {
|
||||
warnStr := ""
|
||||
for _, v := range w {
|
||||
warnStr = warnStr + " " + v
|
||||
}
|
||||
t.Fatalf("bad: %s", warnStr)
|
||||
}
|
||||
if len(e) > 0 {
|
||||
t.Fatalf("bad: %s", e)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Validate_varRef(t *testing.T) {
|
||||
m := testModule(t, "validate-variable-ref")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
resource "aws_instance" "foo" {
|
||||
num = "2"
|
||||
provisioner "shell" {
|
||||
command = "echo hi"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_instance" "bar" {
|
||||
foo = "bar"
|
||||
provisioner "shell" {
|
||||
command = "echo hi"
|
||||
}
|
||||
}
|
|
@ -28,9 +28,10 @@ func (t *TargetsTransformer) Transform(g *Graph) error {
|
|||
}
|
||||
|
||||
for _, v := range g.Vertices() {
|
||||
if targetedNodes.Include(v) {
|
||||
} else {
|
||||
g.Remove(v)
|
||||
if _, ok := v.(GraphNodeAddressable); ok {
|
||||
if !targetedNodes.Include(v) {
|
||||
g.Remove(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,35 +50,29 @@ func (t *TargetsTransformer) parseTargetAddresses() ([]ResourceAddress, error) {
|
|||
return addrs, nil
|
||||
}
|
||||
|
||||
// Returns the list of targeted nodes. A targeted node is either addressed
|
||||
// directly, or is an Ancestor of a targeted node. Destroy mode keeps
|
||||
// Descendents instead of Ancestors.
|
||||
func (t *TargetsTransformer) selectTargetedNodes(
|
||||
g *Graph, addrs []ResourceAddress) (*dag.Set, error) {
|
||||
targetedNodes := new(dag.Set)
|
||||
for _, v := range g.Vertices() {
|
||||
// Keep all providers; they'll be pruned later if necessary
|
||||
if r, ok := v.(GraphNodeProvider); ok {
|
||||
targetedNodes.Add(r)
|
||||
continue
|
||||
}
|
||||
if t.nodeIsTarget(v, addrs) {
|
||||
targetedNodes.Add(v)
|
||||
|
||||
// For the remaining filter, we only care about addressable nodes
|
||||
r, ok := v.(GraphNodeAddressable)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if t.nodeIsTarget(r, addrs) {
|
||||
targetedNodes.Add(r)
|
||||
// If the node would like to know about targets, tell it.
|
||||
if n, ok := r.(GraphNodeTargetable); ok {
|
||||
n.SetTargets(addrs)
|
||||
// We inform nodes that ask about the list of targets - helps for nodes
|
||||
// that need to dynamically expand. Note that this only occurs for nodes
|
||||
// that are already directly targeted.
|
||||
if tn, ok := v.(GraphNodeTargetable); ok {
|
||||
tn.SetTargets(addrs)
|
||||
}
|
||||
|
||||
var deps *dag.Set
|
||||
var err error
|
||||
if t.Destroy {
|
||||
deps, err = g.Descendents(r)
|
||||
deps, err = g.Descendents(v)
|
||||
} else {
|
||||
deps, err = g.Ancestors(r)
|
||||
deps, err = g.Ancestors(v)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -92,7 +87,11 @@ func (t *TargetsTransformer) selectTargetedNodes(
|
|||
}
|
||||
|
||||
func (t *TargetsTransformer) nodeIsTarget(
|
||||
r GraphNodeAddressable, addrs []ResourceAddress) bool {
|
||||
v dag.Vertex, addrs []ResourceAddress) bool {
|
||||
r, ok := v.(GraphNodeAddressable)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
addr := r.ResourceAddress()
|
||||
for _, targetAddr := range addrs {
|
||||
if targetAddr.Equals(addr) {
|
||||
|
|
Loading…
Reference in New Issue