core: -target option to also select resources in descendant modules
Previously the behavior for -target when given a module address was to target only resources directly within that module, ignoring any resources defined in child modules. This behavior turned out to be counter-intuitive, since users expected the -target address to be interpreted hierarchically. We'll now use the new "Contains" function for addresses, which provides a hierarchical "containment" concept that is more consistent with user expectations. In particular, it allows module.foo to match module.foo.module.bar.aws_instance.baz, where before that would not have been true. Since Contains isn't commutative (unlike Equals) this requires some special handling for targeting specific indices. When given an argument like -target=aws_instance.foo[0], the initial graph construction (for both plan and refresh) is for the resource nodes from configuration, which have not yet been expanded to separate indexed instances. Thus we need to do the first pass of TargetsTransformer in mode where indices are ignored, with the work then completed by the DynamicExpand method which re-applies the TargetsTransformer in index-sensitive mode. This is a breaking change for anyone depending on the previous behavior of -target, since it will now select more resources than before. There is no way provided to obtain the previous behavior. Eventually we may support negative targeting, which could then combine with positive targets to regain the previous behavior as an explicit choice.
This commit is contained in:
parent
d3eb2b2d28
commit
a8c58b081c
|
@ -8719,3 +8719,45 @@ func TestContext2Apply_multiRef(t *testing.T) {
|
||||||
t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps)
|
t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext2Apply_targetedModuleRecursive(t *testing.T) {
|
||||||
|
m := testModule(t, "apply-targeted-module-recursive")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.ApplyFn = testApplyFn
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
ProviderResolver: ResourceProviderResolverFixed(
|
||||||
|
map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Targets: []string{"module.child"},
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := ctx.Plan(); err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := ctx.Apply()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod := state.ModuleByPath([]string{"root", "child", "subchild"})
|
||||||
|
if mod == nil {
|
||||||
|
t.Fatalf("no subchild module found in the state!\n\n%#v", state)
|
||||||
|
}
|
||||||
|
if len(mod.Resources) != 1 {
|
||||||
|
t.Fatalf("expected 1 resources, got: %#v", mod.Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkStateString(t, state, `
|
||||||
|
<no state>
|
||||||
|
module.child.subchild:
|
||||||
|
aws_instance.foo:
|
||||||
|
ID = foo
|
||||||
|
num = 2
|
||||||
|
type = aws_instance
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
|
@ -117,7 +117,15 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
&CountBoundaryTransformer{},
|
&CountBoundaryTransformer{},
|
||||||
|
|
||||||
// Target
|
// Target
|
||||||
&TargetsTransformer{Targets: b.Targets},
|
&TargetsTransformer{
|
||||||
|
Targets: b.Targets,
|
||||||
|
|
||||||
|
// Resource nodes from config have not yet been expanded for
|
||||||
|
// "count", so we must apply targeting without indices. Exact
|
||||||
|
// targeting will be dealt with later when these resources
|
||||||
|
// DynamicExpand.
|
||||||
|
IgnoreIndices: true,
|
||||||
|
},
|
||||||
|
|
||||||
// Close opened plugin connections
|
// Close opened plugin connections
|
||||||
&CloseProviderTransformer{},
|
&CloseProviderTransformer{},
|
||||||
|
|
|
@ -144,7 +144,15 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
|
||||||
&ReferenceTransformer{},
|
&ReferenceTransformer{},
|
||||||
|
|
||||||
// Target
|
// Target
|
||||||
&TargetsTransformer{Targets: b.Targets},
|
&TargetsTransformer{
|
||||||
|
Targets: b.Targets,
|
||||||
|
|
||||||
|
// Resource nodes from config have not yet been expanded for
|
||||||
|
// "count", so we must apply targeting without indices. Exact
|
||||||
|
// targeting will be dealt with later when these resources
|
||||||
|
// DynamicExpand.
|
||||||
|
IgnoreIndices: true,
|
||||||
|
},
|
||||||
|
|
||||||
// Close opened plugin connections
|
// Close opened plugin connections
|
||||||
&CloseProviderTransformer{},
|
&CloseProviderTransformer{},
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
module "subchild" {
|
||||||
|
source = "./subchild"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
num = "2"
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module "child" {
|
||||||
|
source = "./child"
|
||||||
|
}
|
|
@ -41,6 +41,12 @@ type TargetsTransformer struct {
|
||||||
// that already have the targets parsed
|
// that already have the targets parsed
|
||||||
ParsedTargets []ResourceAddress
|
ParsedTargets []ResourceAddress
|
||||||
|
|
||||||
|
// If set, the index portions of resource addresses will be ignored
|
||||||
|
// for comparison. This is used when transforming a graph where
|
||||||
|
// counted resources have not yet been expanded, since otherwise
|
||||||
|
// the unexpanded nodes (which never have indices) would not match.
|
||||||
|
IgnoreIndices bool
|
||||||
|
|
||||||
// Set to true when we're in a `terraform destroy` or a
|
// Set to true when we're in a `terraform destroy` or a
|
||||||
// `terraform plan -destroy`
|
// `terraform plan -destroy`
|
||||||
Destroy bool
|
Destroy bool
|
||||||
|
@ -199,7 +205,12 @@ func (t *TargetsTransformer) nodeIsTarget(
|
||||||
|
|
||||||
addr := r.ResourceAddr()
|
addr := r.ResourceAddr()
|
||||||
for _, targetAddr := range addrs {
|
for _, targetAddr := range addrs {
|
||||||
if targetAddr.Equals(addr) {
|
if t.IgnoreIndices {
|
||||||
|
// targetAddr is not a pointer, so we can safely mutate it without
|
||||||
|
// interfering with references elsewhere.
|
||||||
|
targetAddr.Index = -1
|
||||||
|
}
|
||||||
|
if targetAddr.Contains(addr) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,9 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
[remote state](/docs/state/remote.html) is used.
|
[remote state](/docs/state/remote.html) is used.
|
||||||
|
|
||||||
* `-target=resource` - A [Resource
|
* `-target=resource` - A [Resource
|
||||||
Address](/docs/internals/resource-addressing.html) to target. Operation will
|
Address](/docs/internals/resource-addressing.html) to target. For more
|
||||||
be limited to this resource and its dependencies. This flag can be used
|
information, see
|
||||||
multiple times.
|
[the targeting docs from `terraform plan`](/docs/commands/plan.html#resource-targeting).
|
||||||
|
|
||||||
* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag
|
* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag
|
||||||
can be set multiple times. Variable values are interpreted as
|
can be set multiple times. Variable values are interpreted as
|
||||||
|
|
|
@ -63,9 +63,8 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
Ignored when [remote state](/docs/state/remote.html) is used.
|
Ignored when [remote state](/docs/state/remote.html) is used.
|
||||||
|
|
||||||
* `-target=resource` - A [Resource
|
* `-target=resource` - A [Resource
|
||||||
Address](/docs/internals/resource-addressing.html) to target. Operation will
|
Address](/docs/internals/resource-addressing.html) to target. This flag can
|
||||||
be limited to this resource and its dependencies. This flag can be used
|
be used multiple times. See below for more information.
|
||||||
multiple times.
|
|
||||||
|
|
||||||
* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag
|
* `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag
|
||||||
can be set multiple times. Variable values are interpreted as
|
can be set multiple times. Variable values are interpreted as
|
||||||
|
@ -78,6 +77,37 @@ The command-line flags are all optional. The list of available flags are:
|
||||||
files specified by `-var-file` override any values in a "terraform.tfvars".
|
files specified by `-var-file` override any values in a "terraform.tfvars".
|
||||||
This flag can be used multiple times.
|
This flag can be used multiple times.
|
||||||
|
|
||||||
|
## Resource Targeting
|
||||||
|
|
||||||
|
The `-target` option can be used to focus Terraform's attention on only a
|
||||||
|
subset of resources.
|
||||||
|
[Resource Address](/docs/internals/resource-addressing.html) syntax is used
|
||||||
|
to specify the constraint. The resource address is interpreted as follows:
|
||||||
|
|
||||||
|
* If the given address has a _resource spec_, only the specified resource
|
||||||
|
is targeted. If the named resource uses `count` and no explicit index
|
||||||
|
is specified in the address, all of the instances sharing the given
|
||||||
|
resource name are targeted.
|
||||||
|
|
||||||
|
* The the given address _does not_ have a resource spec, and instead just
|
||||||
|
specifies a module path, the target applies to all resources in the
|
||||||
|
specified module _and_ all of the descendent modules of the specified
|
||||||
|
module.
|
||||||
|
|
||||||
|
This targeting capability is provided for exceptional circumstances, such
|
||||||
|
as recovering from mistakes or working around Terraform limitations. It
|
||||||
|
is *not recommended* to use `-target` for routine operations, since this can
|
||||||
|
lead to undetected configuration drift and confusion about how the true state
|
||||||
|
of resources relates to configuration.
|
||||||
|
|
||||||
|
Instead of using `-target` as a means to operate on isolated portions of very
|
||||||
|
large configurations, prefer instead to break large configurations into
|
||||||
|
several smaller configurations that can each be independently applied.
|
||||||
|
[Data sources](/docs/configuration/data-sources.html) can be used to access
|
||||||
|
information about resources created in other configurations, allowing
|
||||||
|
a complex system architecture to be broken down into more managable parts
|
||||||
|
that can be updated independently.
|
||||||
|
|
||||||
## Security Warning
|
## Security Warning
|
||||||
|
|
||||||
Saved plan files (with the `-out` flag) encode the configuration,
|
Saved plan files (with the `-out` flag) encode the configuration,
|
||||||
|
|
Loading…
Reference in New Issue