Merge pull request #25500 from hashicorp/jbardin/destroy-output-expand
Do not evaluate output when doing a full destroy
This commit is contained in:
commit
8e79611e59
|
@ -6275,13 +6275,24 @@ func TestContext2Apply_destroyWithModuleVariableAndCountNested(t *testing.T) {
|
||||||
|
|
||||||
func TestContext2Apply_destroyOutputs(t *testing.T) {
|
func TestContext2Apply_destroyOutputs(t *testing.T) {
|
||||||
m := testModule(t, "apply-destroy-outputs")
|
m := testModule(t, "apply-destroy-outputs")
|
||||||
p := testProvider("aws")
|
p := testProvider("test")
|
||||||
p.ApplyFn = testApplyFn
|
p.ApplyFn = testApplyFn
|
||||||
p.DiffFn = testDiffFn
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
|
p.ReadDataSourceFn = func(req providers.ReadDataSourceRequest) providers.ReadDataSourceResponse {
|
||||||
|
// add the required id
|
||||||
|
m := req.Config.AsValueMap()
|
||||||
|
m["id"] = cty.StringVal("foo")
|
||||||
|
|
||||||
|
return providers.ReadDataSourceResponse{
|
||||||
|
State: cty.ObjectVal(m),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx := testContext2(t, &ContextOpts{
|
ctx := testContext2(t, &ContextOpts{
|
||||||
Config: m,
|
Config: m,
|
||||||
Providers: map[addrs.Provider]providers.Factory{
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -6302,7 +6313,7 @@ func TestContext2Apply_destroyOutputs(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
Config: m,
|
Config: m,
|
||||||
Providers: map[addrs.Provider]providers.Factory{
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -6326,7 +6337,7 @@ func TestContext2Apply_destroyOutputs(t *testing.T) {
|
||||||
State: state,
|
State: state,
|
||||||
Config: m,
|
Config: m,
|
||||||
Providers: map[addrs.Provider]providers.Factory{
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if _, diags := ctx.Plan(); diags.HasErrors() {
|
if _, diags := ctx.Plan(); diags.HasErrors() {
|
||||||
|
|
|
@ -165,7 +165,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
|
||||||
// directly. A destroy is identical to a normal apply, except for the
|
// directly. A destroy is identical to a normal apply, except for the
|
||||||
// fact that we also have configuration to evaluate. While the rest of
|
// fact that we also have configuration to evaluate. While the rest of
|
||||||
// the unused nodes can be programmatically pruned (via
|
// the unused nodes can be programmatically pruned (via
|
||||||
// pruneUnusedNodesTransformer), root module outputs only have an
|
// pruneUnusedNodesTransformer), root module outputs always have an
|
||||||
// implied dependency on remote state. This means that if they exist in
|
// implied dependency on remote state. This means that if they exist in
|
||||||
// the configuration, the only signal to remove them is via the destroy
|
// the configuration, the only signal to remove them is via the destroy
|
||||||
// command itself.
|
// command itself.
|
||||||
|
|
|
@ -1,12 +1,26 @@
|
||||||
resource "aws_instance" "foo" {
|
data "test_data_source" "foo" {
|
||||||
num = "2"
|
foo = "ok"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_instance" "bar" {
|
locals {
|
||||||
foo = "{aws_instance.foo.num}"
|
l = [
|
||||||
dep = "foo"
|
{
|
||||||
|
name = data.test_data_source.foo.id
|
||||||
|
val = "null"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
m = { for v in local.l :
|
||||||
|
v.name => v
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output "foo" {
|
resource "test_instance" "bar" {
|
||||||
value = "${aws_instance.foo.id}"
|
for_each = local.m
|
||||||
|
foo = format("%s", each.value.name)
|
||||||
|
dep = each.value.val
|
||||||
|
}
|
||||||
|
|
||||||
|
output "out" {
|
||||||
|
value = test_instance.bar
|
||||||
}
|
}
|
||||||
|
|
|
@ -207,10 +207,10 @@ func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error {
|
||||||
modules = append(modules, mod)
|
modules = append(modules, mod)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort them by path length, longest first, so that start with the deepest
|
// Sort them by path length, longest first, so that we start with the
|
||||||
// modules. The order of modules at the same tree level doesn't matter, we
|
// deepest modules. The order of modules at the same tree level doesn't
|
||||||
// just need to ensure that child modules are processed before parent
|
// matter, we just need to ensure that child modules are processed before
|
||||||
// modules.
|
// parent modules.
|
||||||
sort.Slice(modules, func(i, j int) bool {
|
sort.Slice(modules, func(i, j int) bool {
|
||||||
return len(modules[i].addr) > len(modules[j].addr)
|
return len(modules[i].addr) > len(modules[j].addr)
|
||||||
})
|
})
|
||||||
|
|
|
@ -88,13 +88,14 @@ func (t *destroyRootOutputTransformer) Transform(g *Graph) error {
|
||||||
|
|
||||||
deps := g.UpEdges(v)
|
deps := g.UpEdges(v)
|
||||||
|
|
||||||
// the destroy node must depend on the eval node
|
|
||||||
deps.Add(v)
|
|
||||||
|
|
||||||
for _, d := range deps {
|
for _, d := range deps {
|
||||||
log.Printf("[TRACE] %s depends on %s", node.Name(), dag.VertexName(d))
|
log.Printf("[TRACE] %s depends on %s", node.Name(), dag.VertexName(d))
|
||||||
g.Connect(dag.BasicEdge(node, d))
|
g.Connect(dag.BasicEdge(node, d))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We no longer need the expand node, since we intend to remove this
|
||||||
|
// output from the state.
|
||||||
|
g.Remove(v)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue