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 {
|
||||
otherStep := to[i]
|
||||
if ourStep != otherStep {
|
||||
|
||||
if ourStep.Name != otherStep.Name {
|
||||
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
|
||||
|
||||
|
|
|
@ -20,13 +20,6 @@ func TestTargetContains(t *testing.T) {
|
|||
mustParseTarget("module.foo"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
// module.foo is an unkeyed module instance here, so it cannot
|
||||
// contain another instance
|
||||
mustParseTarget("module.foo"),
|
||||
mustParseTarget("module.foo[0]"),
|
||||
false,
|
||||
},
|
||||
{
|
||||
RootModuleInstance,
|
||||
mustParseTarget("module.foo"),
|
||||
|
@ -99,6 +92,11 @@ func TestTargetContains(t *testing.T) {
|
|||
mustParseTarget("module.bar.test_resource.foo[0]"),
|
||||
true,
|
||||
},
|
||||
{
|
||||
mustParseTarget("module.bax"),
|
||||
mustParseTarget("module.bax[0].test_resource.foo[0]"),
|
||||
true,
|
||||
},
|
||||
|
||||
// Config paths, while never returned from parsing a target, must still
|
||||
// be targetable
|
||||
|
|
|
@ -5882,3 +5882,68 @@ output"out" {
|
|||
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.dependsOn = n.dependsOn
|
||||
a.forceDependsOn = n.forceDependsOn
|
||||
a.Targets = n.Targets
|
||||
|
||||
return &NodeRefreshableDataResourceInstance{
|
||||
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 {
|
||||
if len(n.Addr) == 0 {
|
||||
return "root"
|
||||
|
|
|
@ -23,6 +23,7 @@ var (
|
|||
_ GraphNodeConfigResource = (*nodeExpandApplyableResource)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*nodeExpandApplyableResource)(nil)
|
||||
_ graphNodeExpandsInstances = (*nodeExpandApplyableResource)(nil)
|
||||
_ GraphNodeTargetable = (*nodeExpandApplyableResource)(nil)
|
||||
)
|
||||
|
||||
func (n *nodeExpandApplyableResource) expandsInstances() {}
|
||||
|
|
|
@ -29,6 +29,7 @@ var (
|
|||
_ GraphNodeReferencer = (*nodeExpandPlannableResource)(nil)
|
||||
_ GraphNodeConfigResource = (*nodeExpandPlannableResource)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil)
|
||||
_ GraphNodeTargetable = (*nodeExpandPlannableResource)(nil)
|
||||
)
|
||||
|
||||
func (n *nodeExpandPlannableResource) Name() string {
|
||||
|
|
|
@ -43,10 +43,6 @@ type TargetsTransformer struct {
|
|||
// 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
|
||||
// `terraform plan -destroy`
|
||||
Destroy bool
|
||||
}
|
||||
|
||||
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
|
||||
// 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) {
|
||||
targetedNodes := make(dag.Set)
|
||||
|
||||
|
@ -95,13 +91,7 @@ func (t *TargetsTransformer) selectTargetedNodes(g *Graph, addrs []addrs.Targeta
|
|||
tn.SetTargets(addrs)
|
||||
}
|
||||
|
||||
var deps dag.Set
|
||||
var err error
|
||||
if t.Destroy {
|
||||
deps, err = g.Descendents(v)
|
||||
} else {
|
||||
deps, err = g.Ancestors(v)
|
||||
}
|
||||
deps, err := g.Ancestors(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -239,8 +229,11 @@ func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetab
|
|||
// vertexAddr because instance addresses are contained within
|
||||
// their associated resources, and so .TargetContains will take
|
||||
// care of this for us.
|
||||
if instance, isInstance := targetAddr.(addrs.AbsResourceInstance); isInstance {
|
||||
targetAddr = instance.ContainingResource()
|
||||
switch instance := targetAddr.(type) {
|
||||
case addrs.AbsResourceInstance:
|
||||
targetAddr = instance.ContainingResource().Config()
|
||||
case addrs.ModuleInstance:
|
||||
targetAddr = instance.Module()
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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