Merge pull request #25206 from hashicorp/jbardin/target-with-expansion
Targeting with module expansion
This commit is contained in:
commit
22680d7409
|
@ -414,9 +414,26 @@ func (m ModuleInstance) TargetContains(other Targetable) bool {
|
||||||
}
|
}
|
||||||
for i, ourStep := range m {
|
for i, ourStep := range m {
|
||||||
otherStep := to[i]
|
otherStep := to[i]
|
||||||
if ourStep != otherStep {
|
|
||||||
|
if ourStep.Name != otherStep.Name {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if this is our last step, because all targets are parsed as
|
||||||
|
// instances, this may be a ModuleInstance intended to be used as a
|
||||||
|
// Module.
|
||||||
|
if i == len(m)-1 {
|
||||||
|
if ourStep.InstanceKey == NoKey {
|
||||||
|
// If the other step is a keyed instance, then we contain that
|
||||||
|
// step, and if it isn't it's a match, which is true either way
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ourStep.InstanceKey != otherStep.InstanceKey {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
|
@ -20,13 +20,6 @@ func TestTargetContains(t *testing.T) {
|
||||||
mustParseTarget("module.foo"),
|
mustParseTarget("module.foo"),
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// module.foo is an unkeyed module instance here, so it cannot
|
|
||||||
// contain another instance
|
|
||||||
mustParseTarget("module.foo"),
|
|
||||||
mustParseTarget("module.foo[0]"),
|
|
||||||
false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
RootModuleInstance,
|
RootModuleInstance,
|
||||||
mustParseTarget("module.foo"),
|
mustParseTarget("module.foo"),
|
||||||
|
@ -99,6 +92,11 @@ func TestTargetContains(t *testing.T) {
|
||||||
mustParseTarget("module.bar.test_resource.foo[0]"),
|
mustParseTarget("module.bar.test_resource.foo[0]"),
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
mustParseTarget("module.bax"),
|
||||||
|
mustParseTarget("module.bax[0].test_resource.foo[0]"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
|
||||||
// Config paths, while never returned from parsing a target, must still
|
// Config paths, while never returned from parsing a target, must still
|
||||||
// be targetable
|
// be targetable
|
||||||
|
|
|
@ -5882,3 +5882,68 @@ output"out" {
|
||||||
t.Fatal(diags.ErrWithWarnings())
|
t.Fatal(diags.ErrWithWarnings())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContext2Plan_targetExpandedAddress(t *testing.T) {
|
||||||
|
m := testModuleInline(t, map[string]string{
|
||||||
|
"main.tf": `
|
||||||
|
module "mod" {
|
||||||
|
count = 3
|
||||||
|
source = "./mod"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
"mod/main.tf": `
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
count = 2
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
|
||||||
|
targets := []addrs.Targetable{}
|
||||||
|
target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo[0]")
|
||||||
|
if diags.HasErrors() {
|
||||||
|
t.Fatal(diags.ErrWithWarnings())
|
||||||
|
}
|
||||||
|
targets = append(targets, target.Subject)
|
||||||
|
|
||||||
|
target, diags = addrs.ParseTargetStr("module.mod[2]")
|
||||||
|
if diags.HasErrors() {
|
||||||
|
t.Fatal(diags.ErrWithWarnings())
|
||||||
|
}
|
||||||
|
targets = append(targets, target.Subject)
|
||||||
|
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Config: m,
|
||||||
|
Providers: map[addrs.Provider]providers.Factory{
|
||||||
|
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
Targets: targets,
|
||||||
|
})
|
||||||
|
|
||||||
|
plan, diags := ctx.Plan()
|
||||||
|
if diags.HasErrors() {
|
||||||
|
t.Fatal(diags.ErrWithWarnings())
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := map[string]plans.Action{
|
||||||
|
// the single targeted mod[1] instances
|
||||||
|
`module.mod[1].aws_instance.foo[0]`: plans.Create,
|
||||||
|
// the whole mode[2]
|
||||||
|
`module.mod[2].aws_instance.foo[0]`: plans.Create,
|
||||||
|
`module.mod[2].aws_instance.foo[1]`: plans.Create,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, res := range plan.Changes.Resources {
|
||||||
|
want := expected[res.Addr.String()]
|
||||||
|
if res.Action != want {
|
||||||
|
t.Fatalf("expected %s action, got: %q %s", want, res.Addr, res.Action)
|
||||||
|
}
|
||||||
|
delete(expected, res.Addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
for res, action := range expected {
|
||||||
|
t.Errorf("missing %s change for %s", action, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -123,6 +123,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
|
||||||
a.ProviderMetas = n.ProviderMetas
|
a.ProviderMetas = n.ProviderMetas
|
||||||
a.dependsOn = n.dependsOn
|
a.dependsOn = n.dependsOn
|
||||||
a.forceDependsOn = n.forceDependsOn
|
a.forceDependsOn = n.forceDependsOn
|
||||||
|
a.Targets = n.Targets
|
||||||
|
|
||||||
return &NodeRefreshableDataResourceInstance{
|
return &NodeRefreshableDataResourceInstance{
|
||||||
NodeAbstractResourceInstance: a,
|
NodeAbstractResourceInstance: a,
|
||||||
|
|
|
@ -145,6 +145,10 @@ func (n *nodeCloseModule) ReferenceableAddrs() []addrs.Referenceable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *nodeCloseModule) TargetDownstream(targeted, untargeted dag.Set) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (n *nodeCloseModule) Name() string {
|
func (n *nodeCloseModule) Name() string {
|
||||||
if len(n.Addr) == 0 {
|
if len(n.Addr) == 0 {
|
||||||
return "root"
|
return "root"
|
||||||
|
|
|
@ -23,6 +23,7 @@ var (
|
||||||
_ GraphNodeConfigResource = (*nodeExpandApplyableResource)(nil)
|
_ GraphNodeConfigResource = (*nodeExpandApplyableResource)(nil)
|
||||||
_ GraphNodeAttachResourceConfig = (*nodeExpandApplyableResource)(nil)
|
_ GraphNodeAttachResourceConfig = (*nodeExpandApplyableResource)(nil)
|
||||||
_ graphNodeExpandsInstances = (*nodeExpandApplyableResource)(nil)
|
_ graphNodeExpandsInstances = (*nodeExpandApplyableResource)(nil)
|
||||||
|
_ GraphNodeTargetable = (*nodeExpandApplyableResource)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (n *nodeExpandApplyableResource) expandsInstances() {}
|
func (n *nodeExpandApplyableResource) expandsInstances() {}
|
||||||
|
|
|
@ -29,6 +29,7 @@ var (
|
||||||
_ GraphNodeReferencer = (*nodeExpandPlannableResource)(nil)
|
_ GraphNodeReferencer = (*nodeExpandPlannableResource)(nil)
|
||||||
_ GraphNodeConfigResource = (*nodeExpandPlannableResource)(nil)
|
_ GraphNodeConfigResource = (*nodeExpandPlannableResource)(nil)
|
||||||
_ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil)
|
_ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil)
|
||||||
|
_ GraphNodeTargetable = (*nodeExpandPlannableResource)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (n *nodeExpandPlannableResource) Name() string {
|
func (n *nodeExpandPlannableResource) Name() string {
|
||||||
|
|
|
@ -43,10 +43,6 @@ type TargetsTransformer struct {
|
||||||
// counted resources have not yet been expanded, since otherwise
|
// counted resources have not yet been expanded, since otherwise
|
||||||
// the unexpanded nodes (which never have indices) would not match.
|
// the unexpanded nodes (which never have indices) would not match.
|
||||||
IgnoreIndices bool
|
IgnoreIndices bool
|
||||||
|
|
||||||
// Set to true when we're in a `terraform destroy` or a
|
|
||||||
// `terraform plan -destroy`
|
|
||||||
Destroy bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TargetsTransformer) Transform(g *Graph) error {
|
func (t *TargetsTransformer) Transform(g *Graph) error {
|
||||||
|
@ -78,7 +74,7 @@ func (t *TargetsTransformer) Transform(g *Graph) error {
|
||||||
|
|
||||||
// Returns a set of targeted nodes. A targeted node is either addressed
|
// Returns a set of targeted nodes. A targeted node is either addressed
|
||||||
// directly, address indirectly via its container, or it's a dependency of a
|
// directly, address indirectly via its container, or it's a dependency of a
|
||||||
// targeted node. Destroy mode keeps dependents instead of dependencies.
|
// targeted node.
|
||||||
func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targetable) (dag.Set, error) {
|
func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targetable) (dag.Set, error) {
|
||||||
targetedNodes := make(dag.Set)
|
targetedNodes := make(dag.Set)
|
||||||
|
|
||||||
|
@ -95,13 +91,7 @@ func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targeta
|
||||||
tn.SetTargets(addrs)
|
tn.SetTargets(addrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
var deps dag.Set
|
deps, err := g.Ancestors(v)
|
||||||
var err error
|
|
||||||
if t.Destroy {
|
|
||||||
deps, err = g.Descendents(v)
|
|
||||||
} else {
|
|
||||||
deps, err = g.Ancestors(v)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -239,8 +229,11 @@ func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetab
|
||||||
// vertexAddr because instance addresses are contained within
|
// vertexAddr because instance addresses are contained within
|
||||||
// their associated resources, and so .TargetContains will take
|
// their associated resources, and so .TargetContains will take
|
||||||
// care of this for us.
|
// care of this for us.
|
||||||
if instance, isInstance := targetAddr.(addrs.AbsResourceInstance); isInstance {
|
switch instance := targetAddr.(type) {
|
||||||
targetAddr = instance.ContainingResource()
|
case addrs.AbsResourceInstance:
|
||||||
|
targetAddr = instance.ContainingResource().Config()
|
||||||
|
case addrs.ModuleInstance:
|
||||||
|
targetAddr = instance.Module()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if targetAddr.TargetContains(vertexAddr) {
|
if targetAddr.TargetContains(vertexAddr) {
|
||||||
|
|
|
@ -200,55 +200,3 @@ output.grandchild_id (expand)
|
||||||
t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual)
|
t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTargetsTransformer_destroy(t *testing.T) {
|
|
||||||
mod := testModule(t, "transform-targets-destroy")
|
|
||||||
|
|
||||||
g := Graph{Path: addrs.RootModuleInstance}
|
|
||||||
{
|
|
||||||
tf := &ConfigTransformer{Config: mod}
|
|
||||||
if err := tf.Transform(&g); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
transform := &AttachResourceConfigTransformer{Config: mod}
|
|
||||||
if err := transform.Transform(&g); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
transform := &ReferenceTransformer{}
|
|
||||||
if err := transform.Transform(&g); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
transform := &TargetsTransformer{
|
|
||||||
Targets: []addrs.Targetable{
|
|
||||||
addrs.RootModuleInstance.Resource(
|
|
||||||
addrs.ManagedResourceMode, "aws_instance", "me",
|
|
||||||
),
|
|
||||||
},
|
|
||||||
Destroy: true,
|
|
||||||
}
|
|
||||||
if err := transform.Transform(&g); err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := strings.TrimSpace(g.String())
|
|
||||||
expected := strings.TrimSpace(`
|
|
||||||
aws_elb.me
|
|
||||||
aws_instance.me
|
|
||||||
aws_instance.me
|
|
||||||
aws_instance.metoo
|
|
||||||
aws_instance.me
|
|
||||||
`)
|
|
||||||
if actual != expected {
|
|
||||||
t.Fatalf("bad:\n\nexpected:\n%s\n\ngot:\n%s\n", expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue