terraform: outputs should not be included if not targeted

Fixes #10911

Outputs that aren't targeted shouldn't be included in the graph.

This requires passing targets to the apply graph. This is unfortunate
but long term should be removable since I'd like to move output changes
to the diff as well.
This commit is contained in:
Mitchell Hashimoto 2017-02-13 12:52:45 -08:00
parent 911717c9d5
commit 4d6085b46a
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
11 changed files with 150 additions and 9 deletions

View File

@ -215,6 +215,7 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, error) {
State: c.state,
Providers: c.components.ResourceProviders(),
Provisioners: c.components.ResourceProvisioners(),
Targets: c.targets,
Destroy: c.destroy,
Validate: opts.Validate,
}).Build(RootModulePath)

View File

@ -6977,6 +6977,38 @@ module.child:
`)
}
// GH-10911 untargeted outputs should not be in the graph, and therefore
// not execute.
func TestContext2Apply_targetedModuleUnrelatedOutputs(t *testing.T) {
m := testModule(t, "apply-targeted-module-unrelated-outputs")
p := testProvider("aws")
p.ApplyFn = testApplyFn
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Module: m,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
Targets: []string{"module.child2"},
})
if _, err := ctx.Plan(); err != nil {
t.Fatalf("err: %s", err)
}
state, err := ctx.Apply()
if err != nil {
t.Fatalf("err: %s", err)
}
checkStateString(t, state, `
<no state>
module.child2:
aws_instance.foo:
ID = foo
`)
}
func TestContext2Apply_targetedModuleResource(t *testing.T) {
m := testModule(t, "apply-targeted-module-resource")
p := testProvider("aws")

View File

@ -28,6 +28,12 @@ type ApplyGraphBuilder struct {
// Provisioners is the list of provisioners supported.
Provisioners []string
// Targets are resources to target. This is only required to make sure
// unnecessary outputs aren't included in the apply graph. The plan
// builder successfully handles targeting resources. In the future,
// outputs should go into the diff so that this is unnecessary.
Targets []string
// DisableReduce, if true, will not reduce the graph. Great for testing.
DisableReduce bool
@ -114,6 +120,9 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
// Add the node to fix the state count boundaries
&CountBoundaryTransformer{},
// Target
&TargetsTransformer{Targets: b.Targets},
// Single root
&RootTransformer{},
}

View File

@ -95,17 +95,15 @@ func TestApplyGraphBuilder_depCbd(t *testing.T) {
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"aws_instance.A": &InstanceDiff{
Destroy: true,
Attributes: map[string]*ResourceAttrDiff{
"name": &ResourceAttrDiff{
Old: "",
New: "foo",
RequiresNew: true,
},
Resources: map[string]*InstanceDiff{"aws_instance.A": &InstanceDiff{Destroy: true,
Attributes: map[string]*ResourceAttrDiff{
"name": &ResourceAttrDiff{
Old: "",
New: "foo",
RequiresNew: true,
},
},
},
"aws_instance.B": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
@ -440,6 +438,54 @@ func TestApplyGraphBuilder_provisionerDestroy(t *testing.T) {
"null_resource.foo (destroy)")
}
func TestApplyGraphBuilder_targetModule(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
"null_resource.foo": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"name": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
&ModuleDiff{
Path: []string{"root", "child2"},
Resources: map[string]*InstanceDiff{
"null_resource.foo": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"name": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Module: testModule(t, "graph-builder-apply-target-module"),
Diff: diff,
Providers: []string{"null"},
Targets: []string{"module.child2"},
}
g, err := b.Build(RootModulePath)
if err != nil {
t.Fatalf("err: %s", err)
}
testGraphNotContains(t, g, "module.child1.output.instance_id")
}
const testApplyGraphBuilderStr = `
aws_instance.create
provider.aws

View File

@ -28,6 +28,13 @@ func (n *NodeApplyableOutput) Path() []string {
return n.PathValue
}
// RemovableIfNotTargeted
func (n *NodeApplyableOutput) RemoveIfNotTargeted() bool {
// We need to add this so that this node will be removed if
// it isn't targeted or a dependency of a target.
return true
}
// GraphNodeReferenceable
func (n *NodeApplyableOutput) ReferenceableName() []string {
name := fmt.Sprintf("output.%s", n.Config.Name)

View File

@ -0,0 +1,10 @@
variable "instance_id" {
}
output "instance_id" {
value = "${var.instance_id}"
}
resource "aws_instance" "foo" {
foo = "${var.instance_id}"
}

View File

@ -0,0 +1,2 @@
resource "aws_instance" "foo" {
}

View File

@ -0,0 +1,10 @@
resource "aws_instance" "foo" {}
module "child1" {
source = "./child1"
instance_id = "${aws_instance.foo.id}"
}
module "child2" {
source = "./child2"
}

View File

@ -0,0 +1,12 @@
variable "instance_id" {
}
output "instance_id" {
value = "${var.instance_id}"
}
resource "null_resource" "foo" {
triggers = {
instance_id = "${var.instance_id}"
}
}

View File

@ -0,0 +1,2 @@
resource "null_resource" "foo" {
}

View File

@ -0,0 +1,10 @@
resource "null_resource" "foo" {}
module "child1" {
source = "./child1"
instance_id = "${null_resource.foo.id}"
}
module "child2" {
source = "./child2"
}