Merge pull request #25760 from hashicorp/jbardin/target-resource

Generalize target addresses before expansion
This commit is contained in:
James Bardin 2020-08-12 10:28:51 -04:00 committed by GitHub
commit aec0059920
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 28 deletions

View File

@ -5948,6 +5948,61 @@ resource "aws_instance" "foo" {
}
}
func TestContext2Plan_targetResourceInModuleInstance(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
module "mod" {
count = 3
source = "./mod"
}
`,
"mod/main.tf": `
resource "aws_instance" "foo" {
}
`,
})
p := testProvider("aws")
p.DiffFn = testDiffFn
target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo")
if diags.HasErrors() {
t.Fatal(diags.ErrWithWarnings())
}
targets := []addrs.Targetable{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] instance
`module.mod[1].aws_instance.foo`: 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)
}
}
func TestContext2Plan_moduleRefIndex(t *testing.T) {
m := testModuleInline(t, map[string]string{
"main.tf": `
@ -6109,3 +6164,50 @@ resource "test_instance" "b" {
_, diags := ctx.Plan()
assertNoErrors(t, diags)
}
func TestContext2Plan_targetedModuleInstance(t *testing.T) {
m := testModule(t, "plan-targeted")
p := testProvider("aws")
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
Config: m,
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
},
Targets: []addrs.Targetable{
addrs.RootModuleInstance.Child("mod", addrs.IntKey(0)),
},
})
plan, diags := ctx.Plan()
if diags.HasErrors() {
t.Fatalf("unexpected errors: %s", diags.Err())
}
schema := p.GetSchemaReturn.ResourceTypes["aws_instance"]
ty := schema.ImpliedType()
if len(plan.Changes.Resources) != 1 {
t.Fatal("expected 1 changes, got", len(plan.Changes.Resources))
}
for _, res := range plan.Changes.Resources {
ric, err := res.Decode(ty)
if err != nil {
t.Fatal(err)
}
switch i := ric.Addr.String(); i {
case "module.mod[0].aws_instance.foo":
if res.Action != plans.Create {
t.Fatalf("resource %s should be created", i)
}
checkVals(t, objectVal(t, schema, map[string]cty.Value{
"id": cty.UnknownVal(cty.String),
"num": cty.NumberIntVal(2),
"type": cty.StringVal("aws_instance"),
}), ric.After)
default:
t.Fatal("unknown instance:", i)
}
}
}

View File

@ -157,12 +157,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
// Target
&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,
},
// Detect when create_before_destroy must be forced on for a particular

View File

@ -176,12 +176,6 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
// Target
&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

View File

@ -3,5 +3,10 @@ resource "aws_instance" "foo" {
}
resource "aws_instance" "bar" {
foo = "${aws_instance.foo.num}"
foo = aws_instance.foo.num
}
module "mod" {
source = "./mod"
count = 1
}

View File

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

View File

@ -22,12 +22,6 @@ type GraphNodeTargetable interface {
type TargetsTransformer struct {
// List of targeted resource names specified by the user
Targets []addrs.Targetable
// 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
}
func (t *TargetsTransformer) Transform(g *Graph) error {
@ -133,25 +127,29 @@ func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetab
vertexAddr = r.ResourceInstanceAddr()
case GraphNodeConfigResource:
vertexAddr = r.ResourceAddr()
default:
// Only resource and resource instance nodes can be targeted.
return false
}
for _, targetAddr := range targets {
if t.IgnoreIndices {
// If we're ignoring indices then we'll convert any resource instance
// addresses into resource addresses. We don't need to convert
// vertexAddr because instance addresses are contained within
// their associated resources, and so .TargetContains will take
// care of this for us.
switch instance := targetAddr.(type) {
switch vertexAddr.(type) {
case addrs.ConfigResource:
// Before expansion happens, we only have nodes that know their
// ConfigResource address. We need to take the more specific
// target addresses and generalize them in order to compare with a
// ConfigResource.
switch target := targetAddr.(type) {
case addrs.AbsResourceInstance:
targetAddr = instance.ContainingResource().Config()
targetAddr = target.ContainingResource().Config()
case addrs.AbsResource:
targetAddr = target.Config()
case addrs.ModuleInstance:
targetAddr = instance.Module()
targetAddr = target.Module()
}
}
if targetAddr.TargetContains(vertexAddr) {
return true
}