Merge pull request #24461 from hashicorp/jbardin/eval-context-path

Module Expansion Activate!
This commit is contained in:
James Bardin 2020-03-26 12:45:23 -04:00 committed by GitHub
commit 34cab3bc99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 525 additions and 458 deletions

View File

@ -213,7 +213,7 @@ func TestApply_destroyTargeted(t *testing.T) {
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance), }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"i-abc123"}`), AttrsJSON: []byte(`{"id":"i-abc123"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
Status: states.ObjectReady, Status: states.ObjectReady,
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{

View File

@ -268,7 +268,7 @@ func testState() *states.State {
// of all of the containing wrapping objects and arrays. // of all of the containing wrapping objects and arrays.
AttrsJSON: []byte("{\n \"id\": \"bar\"\n }"), AttrsJSON: []byte("{\n \"id\": \"bar\"\n }"),
Status: states.ObjectReady, Status: states.ObjectReady,
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
DependsOn: []addrs.Referenceable{}, DependsOn: []addrs.Referenceable{},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
@ -881,10 +881,10 @@ func normalizeJSON(t *testing.T, src []byte) string {
return buf.String() return buf.String()
} }
func mustResourceAddr(s string) addrs.AbsResource { func mustResourceAddr(s string) addrs.ConfigResource {
addr, diags := addrs.ParseAbsResourceStr(s) addr, diags := addrs.ParseAbsResourceStr(s)
if diags.HasErrors() { if diags.HasErrors() {
panic(diags.Err()) panic(diags.Err())
} }
return addr return addr.Config()
} }

View File

@ -277,7 +277,7 @@ func TestRefresh_defaultState(t *testing.T) {
expected := &states.ResourceInstanceObjectSrc{ expected := &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"), AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
DependsOn: []addrs.Referenceable{}, DependsOn: []addrs.Referenceable{},
} }
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {
@ -342,7 +342,7 @@ func TestRefresh_outPath(t *testing.T) {
expected := &states.ResourceInstanceObjectSrc{ expected := &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"), AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
DependsOn: []addrs.Referenceable{}, DependsOn: []addrs.Referenceable{},
} }
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {
@ -572,7 +572,7 @@ func TestRefresh_backup(t *testing.T) {
expected := &states.ResourceInstanceObjectSrc{ expected := &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"changed\"\n }"), AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"changed\"\n }"),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
DependsOn: []addrs.Referenceable{}, DependsOn: []addrs.Referenceable{},
} }
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {
@ -639,7 +639,7 @@ func TestRefresh_disableBackup(t *testing.T) {
expected := &states.ResourceInstanceObjectSrc{ expected := &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"), AttrsJSON: []byte("{\n \"ami\": null,\n \"id\": \"yes\"\n }"),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
DependsOn: []addrs.Referenceable{}, DependsOn: []addrs.Referenceable{},
} }
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {

View File

@ -82,7 +82,7 @@ func TestShow_aliasedProvider(t *testing.T) {
// of all of the containing wrapping objects and arrays. // of all of the containing wrapping objects and arrays.
AttrsJSON: []byte("{\n \"id\": \"bar\"\n }"), AttrsJSON: []byte("{\n \"id\": \"bar\"\n }"),
Status: states.ObjectReady, Status: states.ObjectReady,
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
DependsOn: []addrs.Referenceable{}, DependsOn: []addrs.Referenceable{},
}, },
addrs.RootModuleInstance.ProviderConfigAliased(addrs.NewLegacyProvider("test"), "alias"), addrs.RootModuleInstance.ProviderConfigAliased(addrs.NewLegacyProvider("test"), "alias"),

View File

@ -41,7 +41,7 @@ func TestStateMv(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`),
Status: states.ObjectReady, Status: states.ObjectReady,
Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("test"), Provider: addrs.NewLegacyProvider("test"),
@ -172,7 +172,7 @@ func TestStateMv_resourceToInstance(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`),
Status: states.ObjectReady, Status: states.ObjectReady,
Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("test"), Provider: addrs.NewLegacyProvider("test"),
@ -549,7 +549,7 @@ func TestStateMv_backupExplicit(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`),
Status: states.ObjectReady, Status: states.ObjectReady,
Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("test"), Provider: addrs.NewLegacyProvider("test"),
@ -1068,7 +1068,7 @@ func TestStateMv_withinBackend(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`), AttrsJSON: []byte(`{"id":"foo","foo":"value","bar":"value"}`),
Status: states.ObjectReady, Status: states.ObjectReady,
Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("test"), Provider: addrs.NewLegacyProvider("test"),

View File

@ -68,26 +68,10 @@ func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagno
if attr, exists := content.Attributes["count"]; exists { if attr, exists := content.Attributes["count"]; exists {
mc.Count = attr.Expr mc.Count = attr.Expr
// We currently parse this, but don't yet do anything with it.
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Reserved argument name in module block",
Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name),
Subject: &attr.NameRange,
})
} }
if attr, exists := content.Attributes["for_each"]; exists { if attr, exists := content.Attributes["for_each"]; exists {
mc.ForEach = attr.Expr mc.ForEach = attr.Expr
// We currently parse this, but don't yet do anything with it.
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Reserved argument name in module block",
Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name),
Subject: &attr.NameRange,
})
} }
if attr, exists := content.Attributes["depends_on"]; exists { if attr, exists := content.Attributes["depends_on"]; exists {

View File

@ -20,8 +20,6 @@ func TestLoadModuleCall(t *testing.T) {
file, diags := parser.LoadConfigFile("module-calls.tf") file, diags := parser.LoadConfigFile("module-calls.tf")
assertExactDiagnostics(t, diags, []string{ assertExactDiagnostics(t, diags, []string{
`module-calls.tf:19,3-8: Reserved argument name in module block; The name "count" is reserved for use in a future version of Terraform.`,
`module-calls.tf:20,3-11: Reserved argument name in module block; The name "for_each" is reserved for use in a future version of Terraform.`,
`module-calls.tf:22,3-13: Reserved argument name in module block; The name "depends_on" is reserved for use in a future version of Terraform.`, `module-calls.tf:22,3-13: Reserved argument name in module block; The name "depends_on" is reserved for use in a future version of Terraform.`,
}) })

View File

@ -34,7 +34,7 @@ type ResourceInstanceObject struct {
// the dependency relationships for an object whose configuration is no // the dependency relationships for an object whose configuration is no
// longer available, such as if it has been removed from configuration // longer available, such as if it has been removed from configuration
// altogether, or is now deposed. // altogether, or is now deposed.
Dependencies []addrs.AbsResource Dependencies []addrs.ConfigResource
// CreateBeforeDestroy reflects the status of the lifecycle // CreateBeforeDestroy reflects the status of the lifecycle
// create_before_destroy option when this instance was last updated. // create_before_destroy option when this instance was last updated.

View File

@ -53,7 +53,7 @@ type ResourceInstanceObjectSrc struct {
// ResourceInstanceObject. // ResourceInstanceObject.
Private []byte Private []byte
Status ObjectStatus Status ObjectStatus
Dependencies []addrs.AbsResource Dependencies []addrs.ConfigResource
CreateBeforeDestroy bool CreateBeforeDestroy bool
// deprecated // deprecated
DependsOn []addrs.Referenceable DependsOn []addrs.Referenceable

View File

@ -153,9 +153,9 @@ func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
// Some addrs.Referencable implementations are technically mutable, but // Some addrs.Referencable implementations are technically mutable, but
// we treat them as immutable by convention and so we don't deep-copy here. // we treat them as immutable by convention and so we don't deep-copy here.
var dependencies []addrs.AbsResource var dependencies []addrs.ConfigResource
if obj.Dependencies != nil { if obj.Dependencies != nil {
dependencies = make([]addrs.AbsResource, len(obj.Dependencies)) dependencies = make([]addrs.ConfigResource, len(obj.Dependencies))
copy(dependencies, obj.Dependencies) copy(dependencies, obj.Dependencies)
} }
@ -198,9 +198,9 @@ func (obj *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject {
// Some addrs.Referenceable implementations are technically mutable, but // Some addrs.Referenceable implementations are technically mutable, but
// we treat them as immutable by convention and so we don't deep-copy here. // we treat them as immutable by convention and so we don't deep-copy here.
var dependencies []addrs.AbsResource var dependencies []addrs.ConfigResource
if obj.Dependencies != nil { if obj.Dependencies != nil {
dependencies = make([]addrs.AbsResource, len(obj.Dependencies)) dependencies = make([]addrs.ConfigResource, len(obj.Dependencies))
copy(dependencies, obj.Dependencies) copy(dependencies, obj.Dependencies)
} }

View File

@ -141,7 +141,7 @@ func TestStateDeepCopy(t *testing.T) {
SchemaVersion: 1, SchemaVersion: 1,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Private: []byte("private data"), Private: []byte("private data"),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("test"), Provider: addrs.NewDefaultProvider("test"),
@ -159,9 +159,9 @@ func TestStateDeepCopy(t *testing.T) {
SchemaVersion: 1, SchemaVersion: 1,
AttrsJSON: []byte(`{"woozles":"confuzles"}`), AttrsJSON: []byte(`{"woozles":"confuzles"}`),
Private: []byte("private data"), Private: []byte("private data"),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
{ {
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "test_thing", Type: "test_thing",

View File

@ -218,14 +218,14 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) {
{ {
depsRaw := isV4.Dependencies depsRaw := isV4.Dependencies
deps := make([]addrs.AbsResource, 0, len(depsRaw)) deps := make([]addrs.ConfigResource, 0, len(depsRaw))
for _, depRaw := range depsRaw { for _, depRaw := range depsRaw {
addr, addrDiags := addrs.ParseAbsResourceStr(depRaw) addr, addrDiags := addrs.ParseAbsResourceStr(depRaw)
diags = diags.Append(addrDiags) diags = diags.Append(addrDiags)
if addrDiags.HasErrors() { if addrDiags.HasErrors() {
continue continue
} }
deps = append(deps, addr) deps = append(deps, addr.Config())
} }
obj.Dependencies = deps obj.Dependencies = deps
} }

View File

@ -298,7 +298,7 @@ func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"parent"}`), AttrsJSON: []byte(`{"id":"parent"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("module.child.aws_instance.child")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("module.child.aws_instance.child")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`), mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
) )
@ -1273,7 +1273,7 @@ func testContext2Apply_destroyDependsOn(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`), AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("aws_instance.bar")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("aws_instance.bar")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`), mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
) )
@ -1329,7 +1329,7 @@ func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`), AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"), Provider: addrs.NewLegacyProvider("aws"),
@ -1345,14 +1345,14 @@ func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar"}`), AttrsJSON: []byte(`{"id":"bar"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "aws_instance", Type: "aws_instance",
Name: "foo", Name: "foo",
}, },
Module: root.Addr, Module: root.Addr.Module(),
}, },
}, },
}, },
@ -1427,7 +1427,7 @@ func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`), AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{}, Dependencies: []addrs.ConfigResource{},
}, },
addrs.AbsProviderConfig{ addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"), Provider: addrs.NewLegacyProvider("aws"),
@ -1443,14 +1443,14 @@ func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar"}`), AttrsJSON: []byte(`{"id":"bar"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "aws_instance", Type: "aws_instance",
Name: "foo", Name: "foo",
}, },
Module: child.Addr, Module: child.Addr.Module(),
}, },
}, },
}, },
@ -2708,7 +2708,7 @@ func TestContext2Apply_moduleDestroyOrder(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b"}`), AttrsJSON: []byte(`{"id":"b"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("module.child.aws_instance.a")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("module.child.aws_instance.a")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`), mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
) )
@ -3170,8 +3170,8 @@ func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) {
}, },
), ),
Targets: []addrs.Targetable{ Targets: []addrs.Targetable{
addrs.AbsResource{ addrs.ConfigResource{
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "nonexistent", Type: "nonexistent",
@ -8025,7 +8025,7 @@ func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"i-abc123"}`), AttrsJSON: []byte(`{"id":"i-abc123"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("aws_instance.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("aws_instance.foo")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`), mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
) )
@ -8631,14 +8631,14 @@ func TestContext2Apply_createBefore_depends(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`), AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "aws_instance", Type: "aws_instance",
Name: "web", Name: "web",
}, },
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
}, },
}, },
}, },
@ -8764,14 +8764,14 @@ func TestContext2Apply_singleDestroy(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`), AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "aws_instance", Type: "aws_instance",
Name: "web", Name: "web",
}, },
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
}, },
}, },
}, },
@ -10639,22 +10639,22 @@ func TestContext2Apply_cbdCycle(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"a","require_new":"old","foo":"b"}`), AttrsJSON: []byte(`{"id":"a","require_new":"old","foo":"b"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "test_instance", Type: "test_instance",
Name: "b", Name: "b",
}, },
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
}, },
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "test_instance", Type: "test_instance",
Name: "c", Name: "c",
}, },
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
}, },
}, },
}, },
@ -10672,14 +10672,14 @@ func TestContext2Apply_cbdCycle(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b","require_new":"old","foo":"c"}`), AttrsJSON: []byte(`{"id":"b","require_new":"old","foo":"c"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "test_instance", Type: "test_instance",
Name: "c", Name: "c",
}, },
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
}, },
}, },
}, },

View File

@ -429,12 +429,9 @@ func TestContext2Plan_modules(t *testing.T) {
checkVals(t, expected, ric.After) checkVals(t, expected, ric.After)
} }
} }
func TestContext2Plan_moduleCount(t *testing.T) { func TestContext2Plan_moduleExpand(t *testing.T) {
// This test is skipped with count disabled. // Test a smattering of plan expansion behavior
t.Skip() m := testModule(t, "plan-modules-expand")
//FIXME: add for_each and single modules to this test
m := testModule(t, "plan-modules-count")
p := testProvider("aws") p := testProvider("aws")
p.DiffFn = testDiffFn p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{ ctx := testContext2(t, &ContextOpts{
@ -451,31 +448,18 @@ func TestContext2Plan_moduleCount(t *testing.T) {
t.Fatalf("unexpected errors: %s", diags.Err()) t.Fatalf("unexpected errors: %s", diags.Err())
} }
if len(plan.Changes.Resources) != 6 {
t.Error("expected 6 resource in plan, got", len(plan.Changes.Resources))
}
schema := p.GetSchemaReturn.ResourceTypes["aws_instance"] schema := p.GetSchemaReturn.ResourceTypes["aws_instance"]
ty := schema.ImpliedType() ty := schema.ImpliedType()
expectFoo := objectVal(t, schema, map[string]cty.Value{ expected := map[string]struct{}{
"id": cty.UnknownVal(cty.String), `aws_instance.foo["a"]`: struct{}{},
"foo": cty.StringVal("2"), `module.count_child[1].aws_instance.foo[0]`: struct{}{},
"type": cty.StringVal("aws_instance")}, `module.count_child[1].aws_instance.foo[1]`: struct{}{},
) `module.count_child[0].aws_instance.foo[0]`: struct{}{},
`module.count_child[0].aws_instance.foo[1]`: struct{}{},
expectNum := objectVal(t, schema, map[string]cty.Value{ `module.for_each_child["a"].aws_instance.foo[1]`: struct{}{},
"id": cty.UnknownVal(cty.String), `module.for_each_child["a"].aws_instance.foo[0]`: struct{}{},
"num": cty.NumberIntVal(2), }
"type": cty.StringVal("aws_instance"),
})
expectExpansion := objectVal(t, schema, map[string]cty.Value{
"bar": cty.StringVal("baz"),
"id": cty.UnknownVal(cty.String),
"num": cty.NumberIntVal(2),
"type": cty.StringVal("aws_instance"),
})
for _, res := range plan.Changes.Resources { for _, res := range plan.Changes.Resources {
if res.Action != plans.Create { if res.Action != plans.Create {
@ -486,22 +470,14 @@ func TestContext2Plan_moduleCount(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
var expected cty.Value _, ok := expected[ric.Addr.String()]
switch i := ric.Addr.String(); i { if !ok {
case "aws_instance.bar": t.Fatal("unexpected resource:", ric.Addr.String())
expected = expectFoo
case "aws_instance.foo":
expected = expectNum
case "module.child[0].aws_instance.foo[0]",
"module.child[0].aws_instance.foo[1]",
"module.child[1].aws_instance.foo[0]",
"module.child[1].aws_instance.foo[1]":
expected = expectExpansion
default:
t.Fatal("unknown instance:", i)
} }
delete(expected, ric.Addr.String())
checkVals(t, expected, ric.After) }
for addr := range expected {
t.Error("missing resource", addr)
} }
} }

View File

@ -1985,10 +1985,10 @@ func TestRefresh_updateDependencies(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`), AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
// Existing dependencies should not be removed during refresh // Existing dependencies should not be removed during refresh
{ {
Module: addrs.RootModuleInstance, Module: addrs.RootModule,
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "aws_instance", Type: "aws_instance",

View File

@ -45,24 +45,16 @@ func Eval(n EvalNode, ctx EvalContext) (interface{}, error) {
// EvalRaw is like Eval except that it returns all errors, even if they // EvalRaw is like Eval except that it returns all errors, even if they
// signal something normal such as EvalEarlyExitError. // signal something normal such as EvalEarlyExitError.
func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) { func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) {
path := "unknown" log.Printf("[TRACE] eval: %T", n)
if ctx != nil {
path = ctx.Path().String()
}
if path == "" {
path = "<root>"
}
log.Printf("[TRACE] %s: eval: %T", path, n)
output, err := n.Eval(ctx) output, err := n.Eval(ctx)
if err != nil { if err != nil {
switch err.(type) { switch err.(type) {
case EvalEarlyExitError: case EvalEarlyExitError:
log.Printf("[TRACE] %s: eval: %T, early exit err: %s", path, n, err) log.Printf("[TRACE] eval: %T, early exit err: %s", n, err)
case tfdiags.NonFatalError: case tfdiags.NonFatalError:
log.Printf("[WARN] %s: eval: %T, non-fatal err: %s", path, n, err) log.Printf("[WARN] eval: %T, non-fatal err: %s", n, err)
default: default:
log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err) log.Printf("[ERROR] eval: %T, err: %s", n, err)
} }
} }

View File

@ -161,4 +161,8 @@ type EvalContext interface {
// The InstanceExpander is a global object that is shared across all of the // The InstanceExpander is a global object that is shared across all of the
// EvalContext objects for a given configuration. // EvalContext objects for a given configuration.
InstanceExpander() *instances.Expander InstanceExpander() *instances.Expander
// WithPath returns a copy of the context with the internal path set to the
// path argument.
WithPath(path addrs.ModuleInstance) EvalContext
} }

View File

@ -32,6 +32,13 @@ type BuiltinEvalContext struct {
// PathValue is the Path that this context is operating within. // PathValue is the Path that this context is operating within.
PathValue addrs.ModuleInstance PathValue addrs.ModuleInstance
// pathSet indicates that this context was explicitly created for a
// specific path, and can be safely used for evaluation. This lets us
// differentiate between Pathvalue being unset, and the zero value which is
// equivalent to RootModuleInstance. Path and Evaluation methods will
// panic if this is not set.
pathSet bool
// Evaluator is used for evaluating expressions within the scope of this // Evaluator is used for evaluating expressions within the scope of this
// eval context. // eval context.
Evaluator *Evaluator Evaluator *Evaluator
@ -70,6 +77,13 @@ type BuiltinEvalContext struct {
// BuiltinEvalContext implements EvalContext // BuiltinEvalContext implements EvalContext
var _ EvalContext = (*BuiltinEvalContext)(nil) var _ EvalContext = (*BuiltinEvalContext)(nil)
func (ctx *BuiltinEvalContext) WithPath(path addrs.ModuleInstance) EvalContext {
ctx.pathSet = true
newCtx := *ctx
newCtx.PathValue = path
return &newCtx
}
func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} { func (ctx *BuiltinEvalContext) Stopped() <-chan struct{} {
// This can happen during tests. During tests, we just block forever. // This can happen during tests. During tests, we just block forever.
if ctx.StopContext == nil { if ctx.StopContext == nil {
@ -104,12 +118,6 @@ func (ctx *BuiltinEvalContext) Input() UIInput {
} }
func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) { func (ctx *BuiltinEvalContext) InitProvider(addr addrs.AbsProviderConfig) (providers.Interface, error) {
if !addr.Module.Equal(ctx.Path().Module()) {
// This indicates incorrect use of InitProvider: it should be used
// only from the module that the provider configuration belongs to.
panic(fmt.Sprintf("%s initialized by wrong module %s", addr, ctx.Path()))
}
// If we already initialized, it is an error // If we already initialized, it is an error
if p := ctx.Provider(addr); p != nil { if p := ctx.Provider(addr); p != nil {
return nil, fmt.Errorf("%s is already initialized", addr) return nil, fmt.Errorf("%s is already initialized", addr)
@ -145,12 +153,6 @@ func (ctx *BuiltinEvalContext) ProviderSchema(addr addrs.AbsProviderConfig) *Pro
} }
func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error { func (ctx *BuiltinEvalContext) CloseProvider(addr addrs.AbsProviderConfig) error {
if !addr.Module.Equal(ctx.Path().Module()) {
// This indicates incorrect use of CloseProvider: it should be used
// only from the module that the provider configuration belongs to.
panic(fmt.Sprintf("%s closed by wrong module %s", addr, ctx.Path()))
}
ctx.ProviderLock.Lock() ctx.ProviderLock.Lock()
defer ctx.ProviderLock.Unlock() defer ctx.ProviderLock.Unlock()
@ -213,13 +215,7 @@ func (ctx *BuiltinEvalContext) ProviderInput(pc addrs.AbsProviderConfig) map[str
func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) { func (ctx *BuiltinEvalContext) SetProviderInput(pc addrs.AbsProviderConfig, c map[string]cty.Value) {
absProvider := pc absProvider := pc
if !absProvider.Module.Equal(ctx.Path().Module()) { if !pc.Module.IsRoot() {
// This indicates incorrect use of InitProvider: it should be used
// only from the module that the provider configuration belongs to.
panic(fmt.Sprintf("%s initialized by wrong module %s", absProvider, ctx.Path()))
}
if !ctx.Path().IsRoot() {
// Only root module provider configurations can have input. // Only root module provider configurations can have input.
log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module") log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module")
return return
@ -291,6 +287,9 @@ func (ctx *BuiltinEvalContext) EvaluateExpr(expr hcl.Expression, wantType cty.Ty
} }
func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope { func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData InstanceKeyEvalData) *lang.Scope {
if !ctx.pathSet {
panic("context path not set")
}
data := &evaluationStateData{ data := &evaluationStateData{
Evaluator: ctx.Evaluator, Evaluator: ctx.Evaluator,
ModulePath: ctx.PathValue, ModulePath: ctx.PathValue,
@ -301,6 +300,9 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData
} }
func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance { func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance {
if !ctx.pathSet {
panic("context path not set")
}
return ctx.PathValue return ctx.PathValue
} }
@ -308,6 +310,10 @@ func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance
ctx.VariableValuesLock.Lock() ctx.VariableValuesLock.Lock()
defer ctx.VariableValuesLock.Unlock() defer ctx.VariableValuesLock.Unlock()
if !ctx.pathSet {
panic("context path not set")
}
childPath := n.ModuleInstance(ctx.PathValue) childPath := n.ModuleInstance(ctx.PathValue)
key := childPath.String() key := childPath.String()

View File

@ -15,12 +15,12 @@ func TestBuiltinEvalContextProviderInput(t *testing.T) {
cache := make(map[string]map[string]cty.Value) cache := make(map[string]map[string]cty.Value)
ctx1 := testBuiltinEvalContext(t) ctx1 := testBuiltinEvalContext(t)
ctx1.PathValue = addrs.RootModuleInstance ctx1 = ctx1.WithPath(addrs.RootModuleInstance).(*BuiltinEvalContext)
ctx1.ProviderInputConfig = cache ctx1.ProviderInputConfig = cache
ctx1.ProviderLock = &lock ctx1.ProviderLock = &lock
ctx2 := testBuiltinEvalContext(t) ctx2 := testBuiltinEvalContext(t)
ctx2.PathValue = addrs.RootModuleInstance.Child("child", addrs.NoKey) ctx2 = ctx2.WithPath(addrs.RootModuleInstance.Child("child", addrs.NoKey)).(*BuiltinEvalContext)
ctx2.ProviderInputConfig = cache ctx2.ProviderInputConfig = cache
ctx2.ProviderLock = &lock ctx2.ProviderLock = &lock
@ -56,6 +56,7 @@ func TestBuildingEvalContextInitProvider(t *testing.T) {
testP := &MockProvider{} testP := &MockProvider{}
ctx := testBuiltinEvalContext(t) ctx := testBuiltinEvalContext(t)
ctx = ctx.WithPath(addrs.RootModuleInstance).(*BuiltinEvalContext)
ctx.ProviderLock = &lock ctx.ProviderLock = &lock
ctx.ProviderCache = make(map[string]providers.Interface) ctx.ProviderCache = make(map[string]providers.Interface)
ctx.Components = &basicComponentFactory{ ctx.Components = &basicComponentFactory{

View File

@ -305,6 +305,12 @@ func (c *MockEvalContext) EvaluationScope(self addrs.Referenceable, keyData Inst
return c.EvaluationScopeScope return c.EvaluationScopeScope
} }
func (c *MockEvalContext) WithPath(path addrs.ModuleInstance) EvalContext {
newC := *c
newC.PathPath = path
return &newC
}
func (c *MockEvalContext) Path() addrs.ModuleInstance { func (c *MockEvalContext) Path() addrs.ModuleInstance {
c.PathCalled = true c.PathCalled = true
return c.PathPath return c.PathPath

View File

@ -15,7 +15,7 @@ import (
// EvalDeleteOutput is an EvalNode implementation that deletes an output // EvalDeleteOutput is an EvalNode implementation that deletes an output
// from the state. // from the state.
type EvalDeleteOutput struct { type EvalDeleteOutput struct {
Addr addrs.OutputValue Addr addrs.AbsOutputValue
} }
// TODO: test // TODO: test
@ -25,7 +25,7 @@ func (n *EvalDeleteOutput) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil return nil, nil
} }
state.RemoveOutputValue(n.Addr.Absolute(ctx.Path())) state.RemoveOutputValue(n.Addr)
return nil, nil return nil, nil
} }

View File

@ -205,7 +205,7 @@ type EvalWriteState struct {
// Dependencies are the inter-resource dependencies to be stored in the // Dependencies are the inter-resource dependencies to be stored in the
// state. // state.
Dependencies *[]addrs.AbsResource Dependencies *[]addrs.ConfigResource
} }
func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
@ -452,16 +452,20 @@ func (n *EvalMaybeRestoreDeposedObject) Eval(ctx EvalContext) (interface{}, erro
// in that case, allowing expression evaluation to see it as a zero-element // in that case, allowing expression evaluation to see it as a zero-element
// list rather than as not set at all. // list rather than as not set at all.
type EvalWriteResourceState struct { type EvalWriteResourceState struct {
Addr addrs.ConfigResource Addr addrs.AbsResource
Config *configs.Resource Config *configs.Resource
ProviderAddr addrs.AbsProviderConfig ProviderAddr addrs.AbsProviderConfig
} }
// TODO: test
func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
state := ctx.State() state := ctx.State()
// We'll record our expansion decision in the shared "expander" object
// so that later operations (i.e. DynamicExpand and expression evaluation)
// can refer to it. Since this node represents the abstract module, we need
// to expand the module here to create all resources.
expander := ctx.InstanceExpander()
count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
diags = diags.Append(countDiags) diags = diags.Append(countDiags)
if countDiags.HasErrors() { if countDiags.HasErrors() {
@ -482,25 +486,17 @@ func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) {
if forEach != nil { if forEach != nil {
eachMode = states.EachMap eachMode = states.EachMap
} }
// We'll record our expansion decision in the shared "expander" object
// so that later operations (i.e. DynamicExpand and expression evaluation)
// can refer to it. Since this node represents the abstract module, we need
// to expand the module here to create all resources.
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
// This method takes care of all of the business logic of updating this // This method takes care of all of the business logic of updating this
// while ensuring that any existing instances are preserved, etc. // while ensuring that any existing instances are preserved, etc.
state.SetResourceMeta(n.Addr.Absolute(module), eachMode, n.ProviderAddr) state.SetResourceMeta(n.Addr, eachMode, n.ProviderAddr)
switch eachMode { switch eachMode {
case states.EachList: case states.EachList:
expander.SetResourceCount(module, n.Addr.Resource, count) expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
case states.EachMap: case states.EachMap:
expander.SetResourceForEach(module, n.Addr.Resource, forEach) expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEach)
default: default:
expander.SetResourceSingle(module, n.Addr.Resource) expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
}
} }
return nil, nil return nil, nil
@ -542,7 +538,7 @@ type EvalRefreshDependencies struct {
// Prior State // Prior State
State **states.ResourceInstanceObject State **states.ResourceInstanceObject
// Dependencies to write to the new state // Dependencies to write to the new state
Dependencies *[]addrs.AbsResource Dependencies *[]addrs.ConfigResource
} }
func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) { func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
@ -552,7 +548,7 @@ func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil return nil, nil
} }
depMap := make(map[string]addrs.AbsResource) depMap := make(map[string]addrs.ConfigResource)
for _, d := range *n.Dependencies { for _, d := range *n.Dependencies {
depMap[d.String()] = d depMap[d.String()] = d
} }
@ -566,7 +562,7 @@ func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil return nil, nil
} }
deps := make([]addrs.AbsResource, 0, len(depMap)) deps := make([]addrs.ConfigResource, 0, len(depMap))
for _, d := range depMap { for _, d := range depMap {
deps = append(deps, d) deps = append(deps, d)
} }

View File

@ -35,8 +35,7 @@ func (g *Graph) Walk(walker GraphWalker) tfdiags.Diagnostics {
func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics {
// The callbacks for enter/exiting a graph // The callbacks for enter/exiting a graph
ctx := walker.EnterPath(g.Path) ctx := walker.EvalContext()
defer walker.ExitPath(g.Path)
// Walk the graph. // Walk the graph.
var walkFn dag.WalkFunc var walkFn dag.WalkFunc
@ -54,7 +53,7 @@ func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics {
// is normally the context of our graph but can be overridden // is normally the context of our graph but can be overridden
// with a GraphNodeModuleInstance impl. // with a GraphNodeModuleInstance impl.
vertexCtx := ctx vertexCtx := ctx
if pn, ok := v.(GraphNodeModuleInstance); ok && len(pn.Path()) > 0 { if pn, ok := v.(GraphNodeModuleInstance); ok {
vertexCtx = walker.EnterPath(pn.Path()) vertexCtx = walker.EnterPath(pn.Path())
defer walker.ExitPath(pn.Path()) defer walker.ExitPath(pn.Path())
} }

View File

@ -69,7 +69,7 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
} }
concreteResource := func(a *NodeAbstractResource) dag.Vertex { concreteResource := func(a *NodeAbstractResource) dag.Vertex {
return &NodeApplyableResource{ return &nodeExpandApplyableResource{
NodeAbstractResource: a, NodeAbstractResource: a,
} }
} }

View File

@ -104,7 +104,7 @@ func TestApplyGraphBuilder_depCbd(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -209,7 +209,7 @@ func TestApplyGraphBuilder_doubleCBD(t *testing.T) {
continue continue
} }
switch tv.Addr.Resource.Name { switch tv.Addr.Resource.Resource.Name {
case "A": case "A":
destroyA = fmt.Sprintf("test_object.A (destroy deposed %s)", tv.DeposedKey) destroyA = fmt.Sprintf("test_object.A (destroy deposed %s)", tv.DeposedKey)
case "B": case "B":
@ -273,7 +273,7 @@ func TestApplyGraphBuilder_destroyStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar"}`), AttrsJSON: []byte(`{"id":"bar"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("module.child.test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("module.child.test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -378,7 +378,7 @@ func TestApplyGraphBuilder_moduleDestroy(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo","value":"foo"}`), AttrsJSON: []byte(`{"id":"foo","value":"foo"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("module.A.test_object.foo")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("module.A.test_object.foo")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -566,14 +566,14 @@ func TestApplyGraphBuilder_updateFromOrphan(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`), AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "test_object", Type: "test_object",
Name: "a", Name: "a",
}, },
Module: root.Addr, Module: root.Addr.Module(),
}, },
}, },
}, },
@ -670,14 +670,14 @@ func TestApplyGraphBuilder_updateFromCBDOrphan(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`), AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
addrs.AbsResource{ addrs.ConfigResource{
Resource: addrs.Resource{ Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode, Mode: addrs.ManagedResourceMode,
Type: "test_object", Type: "test_object",
Name: "a", Name: "a",
}, },
Module: root.Addr, Module: root.Addr.Module(),
}, },
}, },
}, },

View File

@ -196,7 +196,7 @@ func (b *PlanGraphBuilder) init() {
} }
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &NodePlannableResource{ return &nodeExpandPlannableResource{
NodeAbstractResource: a, NodeAbstractResource: a,
} }
} }

View File

@ -67,7 +67,7 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
} }
concreteManagedResource := func(a *NodeAbstractResource) dag.Vertex { concreteManagedResource := func(a *NodeAbstractResource) dag.Vertex {
return &NodeRefreshableManagedResource{ return &nodeExpandRefreshableManagedResource{
NodeAbstractResource: a, NodeAbstractResource: a,
} }
} }
@ -87,7 +87,7 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
} }
concreteDataResource := func(a *NodeAbstractResource) dag.Vertex { concreteDataResource := func(a *NodeAbstractResource) dag.Vertex {
return &NodeRefreshableDataResource{ return &nodeExpandRefreshableDataResource{
NodeAbstractResource: a, NodeAbstractResource: a,
} }
} }

View File

@ -102,11 +102,11 @@ provider["registry.terraform.io/-/test"] (close) - *terraform.graphNodeCloseProv
data.test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject data.test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
data.test_object.foo[2] - *terraform.NodeRefreshableManagedResourceInstance data.test_object.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
data.test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject data.test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
test_object.foo - *terraform.NodeRefreshableManagedResource test_object.foo - *terraform.nodeExpandRefreshableManagedResource
test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
test_object.foo - *terraform.NodeRefreshableManagedResource test_object.foo - *terraform.nodeExpandRefreshableManagedResource
provider["registry.terraform.io/-/test"] - *terraform.NodeApplyableProvider provider["registry.terraform.io/-/test"] - *terraform.NodeApplyableProvider
test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
provider["registry.terraform.io/-/test"] - *terraform.NodeApplyableProvider provider["registry.terraform.io/-/test"] - *terraform.NodeApplyableProvider

View File

@ -9,6 +9,7 @@ import (
// GraphWalker is an interface that can be implemented that when used // GraphWalker is an interface that can be implemented that when used
// with Graph.Walk will invoke the given callbacks under certain events. // with Graph.Walk will invoke the given callbacks under certain events.
type GraphWalker interface { type GraphWalker interface {
EvalContext() EvalContext
EnterPath(addrs.ModuleInstance) EvalContext EnterPath(addrs.ModuleInstance) EvalContext
ExitPath(addrs.ModuleInstance) ExitPath(addrs.ModuleInstance)
EnterVertex(dag.Vertex) EnterVertex(dag.Vertex)
@ -22,6 +23,7 @@ type GraphWalker interface {
// implementing all the required functions. // implementing all the required functions.
type NullGraphWalker struct{} type NullGraphWalker struct{}
func (NullGraphWalker) EvalContext() EvalContext { return new(MockEvalContext) }
func (NullGraphWalker) EnterPath(addrs.ModuleInstance) EvalContext { return new(MockEvalContext) } func (NullGraphWalker) EnterPath(addrs.ModuleInstance) EvalContext { return new(MockEvalContext) }
func (NullGraphWalker) ExitPath(addrs.ModuleInstance) {} func (NullGraphWalker) ExitPath(addrs.ModuleInstance) {}
func (NullGraphWalker) EnterVertex(dag.Vertex) {} func (NullGraphWalker) EnterVertex(dag.Vertex) {}

View File

@ -51,8 +51,6 @@ type ContextGraphWalker struct {
} }
func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext { func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
w.once.Do(w.init)
w.contextLock.Lock() w.contextLock.Lock()
defer w.contextLock.Unlock() defer w.contextLock.Unlock()
@ -62,6 +60,14 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
return ctx return ctx
} }
ctx := w.EvalContext().WithPath(path)
w.contexts[key] = ctx.(*BuiltinEvalContext)
return ctx
}
func (w *ContextGraphWalker) EvalContext() EvalContext {
w.once.Do(w.init)
// Our evaluator shares some locks with the main context and the walker // Our evaluator shares some locks with the main context and the walker
// so that we can safely run multiple evaluations at once across // so that we can safely run multiple evaluations at once across
// different modules. // different modules.
@ -78,7 +84,6 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
ctx := &BuiltinEvalContext{ ctx := &BuiltinEvalContext{
StopContext: w.StopContext, StopContext: w.StopContext,
PathValue: path,
Hooks: w.Context.hooks, Hooks: w.Context.hooks,
InputValue: w.Context.uiInput, InputValue: w.Context.uiInput,
InstanceExpanderValue: w.InstanceExpander, InstanceExpanderValue: w.InstanceExpander,
@ -96,7 +101,6 @@ func (w *ContextGraphWalker) EnterPath(path addrs.ModuleInstance) EvalContext {
VariableValuesLock: &w.variableValuesLock, VariableValuesLock: &w.variableValuesLock,
} }
w.contexts[key] = ctx
return ctx return ctx
} }

View File

@ -10,12 +10,45 @@ import (
"github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty"
) )
// NodeRefreshableDataResource represents a resource that is "refreshable". type nodeExpandRefreshableDataResource struct {
type NodeRefreshableDataResource struct {
*NodeAbstractResource *NodeAbstractResource
} }
var ( var (
_ GraphNodeDynamicExpandable = (*nodeExpandRefreshableDataResource)(nil)
_ GraphNodeReferenceable = (*nodeExpandRefreshableDataResource)(nil)
_ GraphNodeReferencer = (*nodeExpandRefreshableDataResource)(nil)
_ GraphNodeConfigResource = (*nodeExpandRefreshableDataResource)(nil)
_ GraphNodeAttachResourceConfig = (*nodeExpandRefreshableDataResource)(nil)
)
func (n *nodeExpandRefreshableDataResource) References() []*addrs.Reference {
return (&NodeRefreshableManagedResource{NodeAbstractResource: n.NodeAbstractResource}).References()
}
func (n *nodeExpandRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
g.Add(&NodeRefreshableDataResource{
NodeAbstractResource: n.NodeAbstractResource,
Addr: n.Addr.Resource.Absolute(module),
})
}
return &g, nil
}
// NodeRefreshableDataResource represents a resource that is "refreshable".
type NodeRefreshableDataResource struct {
*NodeAbstractResource
Addr addrs.AbsResource
}
var (
_ GraphNodeModuleInstance = (*NodeRefreshableDataResource)(nil)
_ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil) _ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil)
_ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil) _ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil)
_ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil) _ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil)
@ -24,6 +57,10 @@ var (
_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil) _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil)
) )
func (n *NodeRefreshableDataResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
// GraphNodeDynamicExpandable // GraphNodeDynamicExpandable
func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) { func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
@ -54,22 +91,18 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
// if we're transitioning whether "count" is set at all. // if we're transitioning whether "count" is set at all.
fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
var instanceAddrs []addrs.AbsResourceInstance
// Inform our instance expander about our expansion results above, // Inform our instance expander about our expansion results above,
// and then use it to calculate the instance addresses we'll expand for. // and then use it to calculate the instance addresses we'll expand for.
expander := ctx.InstanceExpander() expander := ctx.InstanceExpander()
for _, path := range expander.ExpandModule(n.Addr.Module) {
switch { switch {
case count >= 0: case count >= 0:
expander.SetResourceCount(path, n.ResourceAddr().Resource, count) expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
case forEachMap != nil: case forEachMap != nil:
expander.SetResourceForEach(path, n.ResourceAddr().Resource, forEachMap) expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachMap)
default: default:
expander.SetResourceSingle(path, n.ResourceAddr().Resource) expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
}
instanceAddrs = append(instanceAddrs, expander.ExpandResource(n.ResourceAddr().Absolute(path))...)
} }
instanceAddrs := expander.ExpandResource(n.Addr)
// Our graph transformers require access to the full state, so we'll // Our graph transformers require access to the full state, so we'll
// temporarily lock it while we work on this. // temporarily lock it while we work on this.
@ -114,7 +147,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
// directly as NodeDestroyableDataResource. // directly as NodeDestroyableDataResource.
&OrphanResourceCountTransformer{ &OrphanResourceCountTransformer{
Concrete: concreteResourceDestroyable, Concrete: concreteResourceDestroyable,
Addr: n.ResourceAddr(), Addr: n.Addr,
InstanceAddrs: instanceAddrs, InstanceAddrs: instanceAddrs,
State: state, State: state,
}, },

View File

@ -38,11 +38,13 @@ func TestNodeRefreshableDataResourceDynamicExpand_scaleOut(t *testing.T) {
}, },
}) })
addr := addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo")
n := &NodeRefreshableDataResource{ n := &NodeRefreshableDataResource{
NodeAbstractResource: &NodeAbstractResource{ NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo"), Addr: addr,
Config: m.Module.DataResources["data.aws_instance.foo"], Config: m.Module.DataResources["data.aws_instance.foo"],
}, },
Addr: addr.Absolute(addrs.RootModuleInstance),
} }
g, err := n.DynamicExpand(&MockEvalContext{ g, err := n.DynamicExpand(&MockEvalContext{
@ -118,15 +120,17 @@ func TestNodeRefreshableDataResourceDynamicExpand_scaleIn(t *testing.T) {
}, },
}) })
addr := addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo")
n := &NodeRefreshableDataResource{ n := &NodeRefreshableDataResource{
NodeAbstractResource: &NodeAbstractResource{ NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo"), Addr: addr,
Config: m.Module.DataResources["data.aws_instance.foo"], Config: m.Module.DataResources["data.aws_instance.foo"],
ResolvedProvider: addrs.AbsProviderConfig{ ResolvedProvider: addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"), Provider: addrs.NewLegacyProvider("aws"),
Module: addrs.RootModule, Module: addrs.RootModule,
}, },
}, },
Addr: addr.Absolute(addrs.RootModuleInstance),
} }
g, err := n.DynamicExpand(&MockEvalContext{ g, err := n.DynamicExpand(&MockEvalContext{

View File

@ -92,6 +92,11 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
_, call := n.Addr.Call() _, call := n.Addr.Call()
// nodeExpandModule itself does not have visibility into how its ancestors
// were expanded, so we use the expander here to provide all possible paths
// to our module, and register module instances with each of them.
for _, module := range expander.ExpandModule(n.Addr.Parent()) {
ctx = ctx.WithPath(module)
count, countDiags := evaluateResourceCountExpression(n.ModuleCall.Count, ctx) count, countDiags := evaluateResourceCountExpression(n.ModuleCall.Count, ctx)
if countDiags.HasErrors() { if countDiags.HasErrors() {
return nil, countDiags.Err() return nil, countDiags.Err()
@ -110,17 +115,13 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
eachMode = states.EachMap eachMode = states.EachMap
} }
// nodeExpandModule itself does not have visibility into how its ancestors
// were expanded, so we use the expander here to provide all possible paths
// to our module, and register module instances with each of them.
for _, path := range expander.ExpandModule(n.Addr.Parent()) {
switch eachMode { switch eachMode {
case states.EachList: case states.EachList:
expander.SetModuleCount(path, call, count) expander.SetModuleCount(module, call, count)
case states.EachMap: case states.EachMap:
expander.SetModuleForEach(path, call, forEach) expander.SetModuleForEach(module, call, forEach)
default: default:
expander.SetModuleSingle(path, call) expander.SetModuleSingle(module, call)
} }
} }

View File

@ -235,8 +235,7 @@ func (n *NodeApplyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNo
// NodeDestroyableOutput represents an output that is "destroybale": // NodeDestroyableOutput represents an output that is "destroybale":
// its application will remove the output from the state. // its application will remove the output from the state.
type NodeDestroyableOutput struct { type NodeDestroyableOutput struct {
Addr addrs.OutputValue Addr addrs.AbsOutputValue
Module addrs.Module
Config *configs.Output // Config is the output in the config Config *configs.Output // Config is the output in the config
} }
@ -254,7 +253,7 @@ func (n *NodeDestroyableOutput) Name() string {
// GraphNodeModulePath // GraphNodeModulePath
func (n *NodeDestroyableOutput) ModulePath() addrs.Module { func (n *NodeDestroyableOutput) ModulePath() addrs.Module {
return n.Module return n.Addr.Module.Module()
} }
// RemovableIfNotTargeted // RemovableIfNotTargeted

View File

@ -47,7 +47,7 @@ func (n *NodeOutputOrphan) EvalTree() EvalNode {
return &EvalOpFilter{ return &EvalOpFilter{
Ops: []walkOperation{walkRefresh, walkApply, walkDestroy}, Ops: []walkOperation{walkRefresh, walkApply, walkDestroy},
Node: &EvalDeleteOutput{ Node: &EvalDeleteOutput{
Addr: n.Addr.OutputValue, Addr: n.Addr,
}, },
} }
} }

View File

@ -36,7 +36,7 @@ type GraphNodeResourceInstance interface {
// StateDependencies returns any inter-resource dependencies that are // StateDependencies returns any inter-resource dependencies that are
// stored in the state. // stored in the state.
StateDependencies() []addrs.AbsResource StateDependencies() []addrs.ConfigResource
} }
// NodeAbstractResource represents a resource that has no associated // NodeAbstractResource represents a resource that has no associated
@ -78,19 +78,11 @@ var (
_ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil)
) )
func (n *NodeAbstractResource) addr() addrs.AbsResource {
return n.Addr.Absolute(n.Addr.Module.UnkeyedInstanceShim())
}
// NewNodeAbstractResource creates an abstract resource graph node for // NewNodeAbstractResource creates an abstract resource graph node for
// the given absolute resource address. // the given absolute resource address.
func NewNodeAbstractResource(addr addrs.AbsResource) *NodeAbstractResource { func NewNodeAbstractResource(addr addrs.ConfigResource) *NodeAbstractResource {
// FIXME: this should probably accept a ConfigResource
return &NodeAbstractResource{ return &NodeAbstractResource{
Addr: addrs.ConfigResource{ Addr: addr,
Resource: addr.Resource,
Module: addr.Module.Module(),
},
} }
} }
@ -101,14 +93,13 @@ func NewNodeAbstractResource(addr addrs.AbsResource) *NodeAbstractResource {
// the "count" or "for_each" arguments. // the "count" or "for_each" arguments.
type NodeAbstractResourceInstance struct { type NodeAbstractResourceInstance struct {
NodeAbstractResource NodeAbstractResource
ModuleInstance addrs.ModuleInstance Addr addrs.AbsResourceInstance
InstanceKey addrs.InstanceKey
// The fields below will be automatically set using the Attach // The fields below will be automatically set using the Attach
// interfaces if you're running those transforms, but also be explicitly // interfaces if you're running those transforms, but also be explicitly
// set if you already have that information. // set if you already have that information.
ResourceState *states.Resource ResourceState *states.Resource
Dependencies []addrs.AbsResource Dependencies []addrs.ConfigResource
} }
var ( var (
@ -136,15 +127,10 @@ func NewNodeAbstractResourceInstance(addr addrs.AbsResourceInstance) *NodeAbstra
// object and the InstanceKey field in our own struct. The // object and the InstanceKey field in our own struct. The
// ResourceInstanceAddr method will stick these back together again on // ResourceInstanceAddr method will stick these back together again on
// request. // request.
r := NewNodeAbstractResource(addr.ContainingResource().Config())
return &NodeAbstractResourceInstance{ return &NodeAbstractResourceInstance{
NodeAbstractResource: NodeAbstractResource{ NodeAbstractResource: *r,
Addr: addrs.ConfigResource{ Addr: addr,
Resource: addr.Resource.Resource,
Module: addr.Module.Module(),
},
},
ModuleInstance: addr.Module,
InstanceKey: addr.Resource.Key,
} }
} }
@ -156,13 +142,8 @@ func (n *NodeAbstractResourceInstance) Name() string {
return n.ResourceInstanceAddr().String() return n.ResourceInstanceAddr().String()
} }
// GraphNodeModuleInstance
func (n *NodeAbstractResource) Path() addrs.ModuleInstance {
return n.Addr.Module.UnkeyedInstanceShim()
}
func (n *NodeAbstractResourceInstance) Path() addrs.ModuleInstance { func (n *NodeAbstractResourceInstance) Path() addrs.ModuleInstance {
return n.ModuleInstance return n.Addr.Module
} }
// GraphNodeModulePath // GraphNodeModulePath
@ -285,9 +266,9 @@ func dottedInstanceAddr(tr addrs.ResourceInstance) string {
} }
// StateDependencies returns the dependencies saved in the state. // StateDependencies returns the dependencies saved in the state.
func (n *NodeAbstractResourceInstance) StateDependencies() []addrs.AbsResource { func (n *NodeAbstractResourceInstance) StateDependencies() []addrs.ConfigResource {
if rs := n.ResourceState; rs != nil { if rs := n.ResourceState; rs != nil {
if s := rs.Instance(n.InstanceKey); s != nil { if s := rs.Instance(n.Addr.Resource.Key); s != nil {
if s.Current != nil { if s.Current != nil {
return s.Current.Dependencies return s.Current.Dependencies
} }
@ -360,7 +341,7 @@ func (n *NodeAbstractResourceInstance) Provider() addrs.Provider {
return n.Config.Provider return n.Config.Provider
} }
// FIXME: this will be a default provider // FIXME: this will be a default provider
return addrs.NewLegacyProvider(n.Addr.Resource.ImpliedProvider()) return addrs.NewLegacyProvider(n.Addr.Resource.ContainingResource().ImpliedProvider())
} }
// GraphNodeProvisionerConsumer // GraphNodeProvisionerConsumer
@ -395,7 +376,7 @@ func (n *NodeAbstractResource) ResourceAddr() addrs.ConfigResource {
// GraphNodeResourceInstance // GraphNodeResourceInstance
func (n *NodeAbstractResourceInstance) ResourceInstanceAddr() addrs.AbsResourceInstance { func (n *NodeAbstractResourceInstance) ResourceInstanceAddr() addrs.AbsResourceInstance {
return n.NodeAbstractResource.addr().Instance(n.InstanceKey) return n.Addr
} }
// GraphNodeTargetable // GraphNodeTargetable

View File

@ -8,6 +8,44 @@ import (
"github.com/hashicorp/terraform/lang" "github.com/hashicorp/terraform/lang"
) )
// nodeExpandApplyableResource handles the first layer of resource
// expansion during apply. Even though the resource instances themselves are
// already expanded from the plan, we still need to expand the
// NodeApplyableResource nodes into their respective modules.
type nodeExpandApplyableResource struct {
*NodeAbstractResource
}
var (
_ GraphNodeDynamicExpandable = (*nodeExpandApplyableResource)(nil)
_ GraphNodeReferenceable = (*nodeExpandApplyableResource)(nil)
_ GraphNodeReferencer = (*nodeExpandApplyableResource)(nil)
_ GraphNodeConfigResource = (*nodeExpandApplyableResource)(nil)
_ GraphNodeAttachResourceConfig = (*nodeExpandApplyableResource)(nil)
)
func (n *nodeExpandApplyableResource) References() []*addrs.Reference {
return (&NodeApplyableResource{NodeAbstractResource: n.NodeAbstractResource}).References()
}
func (n *nodeExpandApplyableResource) Name() string {
return n.NodeAbstractResource.Name() + " (prepare state)"
}
func (n *nodeExpandApplyableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
g.Add(&NodeApplyableResource{
NodeAbstractResource: n.NodeAbstractResource,
Addr: n.Addr.Resource.Absolute(module),
})
}
return &g, nil
}
// NodeApplyableResource represents a resource that is "applyable": // NodeApplyableResource represents a resource that is "applyable":
// it may need to have its record in the state adjusted to match configuration. // it may need to have its record in the state adjusted to match configuration.
// //
@ -18,9 +56,12 @@ import (
// in the state is suitably prepared to receive any updates to instances. // in the state is suitably prepared to receive any updates to instances.
type NodeApplyableResource struct { type NodeApplyableResource struct {
*NodeAbstractResource *NodeAbstractResource
Addr addrs.AbsResource
} }
var ( var (
_ GraphNodeModuleInstance = (*NodeApplyableResource)(nil)
_ GraphNodeConfigResource = (*NodeApplyableResource)(nil) _ GraphNodeConfigResource = (*NodeApplyableResource)(nil)
_ GraphNodeEvalable = (*NodeApplyableResource)(nil) _ GraphNodeEvalable = (*NodeApplyableResource)(nil)
_ GraphNodeProviderConsumer = (*NodeApplyableResource)(nil) _ GraphNodeProviderConsumer = (*NodeApplyableResource)(nil)
@ -28,8 +69,8 @@ var (
_ GraphNodeReferencer = (*NodeApplyableResource)(nil) _ GraphNodeReferencer = (*NodeApplyableResource)(nil)
) )
func (n *NodeApplyableResource) Name() string { func (n *NodeApplyableResource) Path() addrs.ModuleInstance {
return n.NodeAbstractResource.Name() + " (prepare state)" return n.Addr.Module
} }
func (n *NodeApplyableResource) References() []*addrs.Reference { func (n *NodeApplyableResource) References() []*addrs.Reference {

View File

@ -24,7 +24,7 @@ type NodeApplyableResourceInstance struct {
*NodeAbstractResourceInstance *NodeAbstractResourceInstance
destroyNode GraphNodeDestroyerCBD destroyNode GraphNodeDestroyerCBD
graphNodeDeposer // implementation of GraphNodeDeposer graphNodeDeposer // implementation of GraphNodeDeposerConfig
} }
var ( var (
@ -100,12 +100,7 @@ func (n *NodeApplyableResourceInstance) References() []*addrs.Reference {
// GraphNodeAttachDependencies // GraphNodeAttachDependencies
func (n *NodeApplyableResourceInstance) AttachDependencies(deps []addrs.ConfigResource) { func (n *NodeApplyableResourceInstance) AttachDependencies(deps []addrs.ConfigResource) {
var shimmed []addrs.AbsResource n.Dependencies = deps
for _, r := range deps {
shimmed = append(shimmed, r.Absolute(r.Module.UnkeyedInstanceShim()))
}
n.Dependencies = shimmed
} }
// GraphNodeEvalable // GraphNodeEvalable

View File

@ -26,6 +26,7 @@ type NodeDestroyResourceInstance struct {
} }
var ( var (
_ GraphNodeModuleInstance = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeConfigResource = (*NodeDestroyResourceInstance)(nil) _ GraphNodeConfigResource = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil) _ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil) _ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil)
@ -63,7 +64,7 @@ func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool {
// Otherwise check the state for a stored destroy order // Otherwise check the state for a stored destroy order
if rs := n.ResourceState; rs != nil { if rs := n.ResourceState; rs != nil {
if s := rs.Instance(n.InstanceKey); s != nil { if s := rs.Instance(n.Addr.Resource.Key); s != nil {
if s.Current != nil { if s.Current != nil {
return s.Current.CreateBeforeDestroy return s.Current.CreateBeforeDestroy
} }
@ -136,7 +137,7 @@ func (n *NodeDestroyResourceInstance) EvalTree() EvalNode {
rs := n.ResourceState rs := n.ResourceState
var is *states.ResourceInstance var is *states.ResourceInstance
if rs != nil { if rs != nil {
is = rs.Instance(n.InstanceKey) is = rs.Instance(n.Addr.Resource.Key)
} }
if is == nil { if is == nil {
log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr) log.Printf("[WARN] NodeDestroyResourceInstance for %s with no state", addr)
@ -278,7 +279,7 @@ func (n *NodeDestroyResourceInstance) EvalTree() EvalNode {
} }
} }
// NodeDestroyResourceInstance represents a resource that is to be destroyed. // NodeDestroyResource represents a resource that is to be destroyed.
// //
// Destroying a resource is a state-only operation: it is the individual // Destroying a resource is a state-only operation: it is the individual
// instances being destroyed that affects remote objects. During graph // instances being destroyed that affects remote objects. During graph
@ -292,6 +293,7 @@ type NodeDestroyResource struct {
} }
var ( var (
_ GraphNodeModuleInstance = (*NodeDestroyResource)(nil)
_ GraphNodeConfigResource = (*NodeDestroyResource)(nil) _ GraphNodeConfigResource = (*NodeDestroyResource)(nil)
_ GraphNodeReferenceable = (*NodeDestroyResource)(nil) _ GraphNodeReferenceable = (*NodeDestroyResource)(nil)
_ GraphNodeReferencer = (*NodeDestroyResource)(nil) _ GraphNodeReferencer = (*NodeDestroyResource)(nil)
@ -305,6 +307,10 @@ var (
_ GraphNodeNoProvider = (*NodeDestroyResource)(nil) _ GraphNodeNoProvider = (*NodeDestroyResource)(nil)
) )
func (n *NodeDestroyResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
func (n *NodeDestroyResource) Name() string { func (n *NodeDestroyResource) Name() string {
return n.ResourceAddr().String() + " (clean up state)" return n.ResourceAddr().String() + " (clean up state)"
} }

View File

@ -3,13 +3,16 @@ package terraform
import ( import (
"log" "log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/dag" "github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
// NodePlannableResource represents a resource that is "plannable": // nodeExpandPlannableResource handles the first layer of resource
// it is ready to be planned in order to create a diff. // expansion. We need this extra layer so DynamicExpand is called twice for
type NodePlannableResource struct { // the resource, the first to expand the Resource for each module instance, and
// the second to expand each ResourceInstance for the expanded Resources.
type nodeExpandPlannableResource struct {
*NodeAbstractResource *NodeAbstractResource
// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
@ -19,6 +22,64 @@ type NodePlannableResource struct {
} }
var ( var (
_ GraphNodeDestroyerCBD = (*nodeExpandPlannableResource)(nil)
_ GraphNodeDynamicExpandable = (*nodeExpandPlannableResource)(nil)
_ GraphNodeReferenceable = (*nodeExpandPlannableResource)(nil)
_ GraphNodeReferencer = (*nodeExpandPlannableResource)(nil)
_ GraphNodeConfigResource = (*nodeExpandPlannableResource)(nil)
_ GraphNodeAttachResourceConfig = (*nodeExpandPlannableResource)(nil)
)
// GraphNodeDestroyerCBD
func (n *nodeExpandPlannableResource) CreateBeforeDestroy() bool {
if n.ForceCreateBeforeDestroy != nil {
return *n.ForceCreateBeforeDestroy
}
// If we have no config, we just assume no
if n.Config == nil || n.Config.Managed == nil {
return false
}
return n.Config.Managed.CreateBeforeDestroy
}
// GraphNodeDestroyerCBD
func (n *nodeExpandPlannableResource) ModifyCreateBeforeDestroy(v bool) error {
n.ForceCreateBeforeDestroy = &v
return nil
}
func (n *nodeExpandPlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
g.Add(&NodePlannableResource{
NodeAbstractResource: n.NodeAbstractResource,
Addr: n.Addr.Resource.Absolute(module),
ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy,
})
}
return &g, nil
}
// NodePlannableResource represents a resource that is "plannable":
// it is ready to be planned in order to create a diff.
type NodePlannableResource struct {
*NodeAbstractResource
Addr addrs.AbsResource
// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
// during graph construction, if dependencies require us to force this
// on regardless of what the configuration says.
ForceCreateBeforeDestroy *bool
}
var (
_ GraphNodeModuleInstance = (*NodePlannableResource)(nil)
_ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil) _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil)
_ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil) _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil)
_ GraphNodeReferenceable = (*NodePlannableResource)(nil) _ GraphNodeReferenceable = (*NodePlannableResource)(nil)
@ -27,6 +88,19 @@ var (
_ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil) _ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil)
) )
func (n *NodePlannableResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
func (n *NodePlannableResource) Name() string {
return n.Addr.String()
}
// GraphNodeModuleInstance
func (n *NodePlannableResource) ModuleInstance() addrs.ModuleInstance {
return n.Addr.Module
}
// GraphNodeEvalable // GraphNodeEvalable
func (n *NodePlannableResource) EvalTree() EvalNode { func (n *NodePlannableResource) EvalTree() EvalNode {
if n.Config == nil { if n.Config == nil {
@ -67,26 +141,16 @@ func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
// We need to potentially rename an instance address in the state
// if we're transitioning whether "count" is set at all.
fixResourceCountSetTransition(ctx, n.Addr.Config(), n.Config.Count != nil)
// Our instance expander should already have been informed about the // Our instance expander should already have been informed about the
// expansion of this resource and of all of its containing modules, so // expansion of this resource and of all of its containing modules, so
// it can tell us which instance addresses we need to process. // it can tell us which instance addresses we need to process.
expander := ctx.InstanceExpander() expander := ctx.InstanceExpander()
instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path())) instanceAddrs := expander.ExpandResource(n.ResourceAddr().Absolute(ctx.Path()))
// We need to potentially rename an instance address in the state
// if we're transitioning whether "count" is set at all.
//
// FIXME: We're re-evaluating count here, even though the InstanceExpander
// has already dealt with our expansion above, because we need it to
// call fixResourceCountSetTransition; the expander API and that function
// are not compatible yet.
count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx)
diags = diags.Append(countDiags)
if countDiags.HasErrors() {
return nil, diags.Err()
}
fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
// Our graph transformers require access to the full state, so we'll // Our graph transformers require access to the full state, so we'll
// temporarily lock it while we work on this. // temporarily lock it while we work on this.
state := ctx.State().Lock() state := ctx.State().Lock()
@ -138,7 +202,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// Add the count/for_each orphans // Add the count/for_each orphans
&OrphanResourceCountTransformer{ &OrphanResourceCountTransformer{
Concrete: concreteResourceOrphan, Concrete: concreteResourceOrphan,
Addr: n.ResourceAddr(), Addr: n.Addr,
InstanceAddrs: instanceAddrs, InstanceAddrs: instanceAddrs,
State: state, State: state,
}, },

View File

@ -14,33 +14,75 @@ import (
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
// nodeExpandRefreshableResource handles the first layer of resource
// expansion durin refresh. We need this extra layer so DynamicExpand is called
// twice for the resource, the first to expand the Resource for each module
// instance, and the second to expand each ResourceInstance for the expanded
// Resources.
type nodeExpandRefreshableManagedResource struct {
*NodeAbstractResource
// We attach dependencies to the Resource during refresh, since the
// instances are instantiated during DynamicExpand.
Dependencies []addrs.ConfigResource
}
var (
_ GraphNodeDynamicExpandable = (*nodeExpandRefreshableManagedResource)(nil)
_ GraphNodeReferenceable = (*nodeExpandRefreshableManagedResource)(nil)
_ GraphNodeReferencer = (*nodeExpandRefreshableManagedResource)(nil)
_ GraphNodeConfigResource = (*nodeExpandRefreshableManagedResource)(nil)
_ GraphNodeAttachResourceConfig = (*nodeExpandRefreshableManagedResource)(nil)
_ GraphNodeAttachDependencies = (*nodeExpandRefreshableManagedResource)(nil)
)
// GraphNodeAttachDependencies
func (n *nodeExpandRefreshableManagedResource) AttachDependencies(deps []addrs.ConfigResource) {
n.Dependencies = deps
}
func (n *nodeExpandRefreshableManagedResource) References() []*addrs.Reference {
return (&NodeRefreshableManagedResource{NodeAbstractResource: n.NodeAbstractResource}).References()
}
func (n *nodeExpandRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
g.Add(&NodeRefreshableManagedResource{
NodeAbstractResource: n.NodeAbstractResource,
Addr: n.Addr.Resource.Absolute(module),
Dependencies: n.Dependencies,
})
}
return &g, nil
}
// NodeRefreshableManagedResource represents a resource that is expandable into // NodeRefreshableManagedResource represents a resource that is expandable into
// NodeRefreshableManagedResourceInstance. Resource count orphans are also added. // NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
type NodeRefreshableManagedResource struct { type NodeRefreshableManagedResource struct {
*NodeAbstractResource *NodeAbstractResource
Addr addrs.AbsResource
// We attach dependencies to the Resource during refresh, since the // We attach dependencies to the Resource during refresh, since the
// instances are instantiated during DynamicExpand. // instances are instantiated during DynamicExpand.
Dependencies []addrs.AbsResource Dependencies []addrs.ConfigResource
} }
var ( var (
_ GraphNodeModuleInstance = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeDynamicExpandable = (*NodeRefreshableManagedResource)(nil) _ GraphNodeDynamicExpandable = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeReferenceable = (*NodeRefreshableManagedResource)(nil) _ GraphNodeReferenceable = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeReferencer = (*NodeRefreshableManagedResource)(nil) _ GraphNodeReferencer = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeConfigResource = (*NodeRefreshableManagedResource)(nil) _ GraphNodeConfigResource = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResource)(nil) _ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeAttachDependencies = (*NodeRefreshableManagedResource)(nil)
) )
// GraphNodeAttachDependencies func (n *NodeRefreshableManagedResource) Path() addrs.ModuleInstance {
func (n *NodeRefreshableManagedResource) AttachDependencies(deps []addrs.ConfigResource) { return n.Addr.Module
var shimmed []addrs.AbsResource
for _, r := range deps {
shimmed = append(shimmed, r.Absolute(r.Module.UnkeyedInstanceShim()))
}
n.Dependencies = shimmed
} }
// GraphNodeDynamicExpandable // GraphNodeDynamicExpandable
@ -60,22 +102,20 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
// Next we need to potentially rename an instance address in the state // Next we need to potentially rename an instance address in the state
// if we're transitioning whether "count" is set at all. // if we're transitioning whether "count" is set at all.
fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) fixResourceCountSetTransition(ctx, n.Addr.Config(), count != -1)
// Inform our instance expander about our expansion results above, // Inform our instance expander about our expansion results above,
// and then use it to calculate the instance addresses we'll expand for. // and then use it to calculate the instance addresses we'll expand for.
expander := ctx.InstanceExpander() expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
switch { switch {
case count >= 0: case count >= 0:
expander.SetResourceCount(module, n.ResourceAddr().Resource, count) expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
case forEachMap != nil: case forEachMap != nil:
expander.SetResourceForEach(module, n.ResourceAddr().Resource, forEachMap) expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachMap)
default: default:
expander.SetResourceSingle(module, n.ResourceAddr().Resource) expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
} }
} instanceAddrs := expander.ExpandResource(n.Addr)
instanceAddrs := expander.ExpandModuleResource(n.Addr.Module, n.ResourceAddr().Resource)
// Our graph transformers require access to the full state, so we'll // Our graph transformers require access to the full state, so we'll
// temporarily lock it while we work on this. // temporarily lock it while we work on this.
@ -101,7 +141,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
&ResourceCountTransformer{ &ResourceCountTransformer{
Concrete: concreteResource, Concrete: concreteResource,
Schema: n.Schema, Schema: n.Schema,
Addr: n.ResourceAddr(), Addr: n.Addr.Config(),
InstanceAddrs: instanceAddrs, InstanceAddrs: instanceAddrs,
}, },
@ -109,7 +149,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
// during a scale in. // during a scale in.
&OrphanResourceCountTransformer{ &OrphanResourceCountTransformer{
Concrete: concreteResource, Concrete: concreteResource,
Addr: n.ResourceAddr(), Addr: n.Addr,
InstanceAddrs: instanceAddrs, InstanceAddrs: instanceAddrs,
State: state, State: state,
}, },

View File

@ -40,11 +40,13 @@ func TestNodeRefreshableManagedResourceDynamicExpand_scaleOut(t *testing.T) {
}, },
}).SyncWrapper() }).SyncWrapper()
cfgAddr := addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo")
n := &NodeRefreshableManagedResource{ n := &NodeRefreshableManagedResource{
NodeAbstractResource: &NodeAbstractResource{ NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo"), Addr: cfgAddr,
Config: m.Module.ManagedResources["aws_instance.foo"], Config: m.Module.ManagedResources["aws_instance.foo"],
}, },
Addr: cfgAddr.Absolute(addrs.RootModuleInstance),
} }
g, err := n.DynamicExpand(&MockEvalContext{ g, err := n.DynamicExpand(&MockEvalContext{
@ -120,11 +122,13 @@ func TestNodeRefreshableManagedResourceDynamicExpand_scaleIn(t *testing.T) {
}, },
}).SyncWrapper() }).SyncWrapper()
cfgAddr := addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo")
n := &NodeRefreshableManagedResource{ n := &NodeRefreshableManagedResource{
NodeAbstractResource: &NodeAbstractResource{ NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo"), Addr: cfgAddr,
Config: m.Module.ManagedResources["aws_instance.foo"], Config: m.Module.ManagedResources["aws_instance.foo"],
}, },
Addr: cfgAddr.Absolute(addrs.RootModuleInstance),
} }
g, err := n.DynamicExpand(&MockEvalContext{ g, err := n.DynamicExpand(&MockEvalContext{
@ -160,15 +164,11 @@ func TestNodeRefreshableManagedResourceEvalTree_scaleOut(t *testing.T) {
m := testModule(t, "refresh-resource-scale-inout") m := testModule(t, "refresh-resource-scale-inout")
n := &NodeRefreshableManagedResourceInstance{ n := &NodeRefreshableManagedResourceInstance{
NodeAbstractResourceInstance: &NodeAbstractResourceInstance{ NodeAbstractResourceInstance: NewNodeAbstractResourceInstance(addrs.RootModuleInstance.Resource(addrs.ManagedResourceMode, "aws_instance", "foo").Instance(addrs.IntKey(2))),
NodeAbstractResource: NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo"),
Config: m.Module.ManagedResources["aws_instance.foo"],
},
InstanceKey: addrs.IntKey(2),
},
} }
n.AttachResourceConfig(m.Module.ManagedResources["aws_instance.foo"])
actual := n.EvalTree() actual := n.EvalTree()
expected := n.evalTreeManagedResourceNoState() expected := n.evalTreeManagedResourceNoState()

View File

@ -1,6 +1,7 @@
package terraform package terraform
import ( import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema" "github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/providers" "github.com/hashicorp/terraform/providers"
@ -15,6 +16,7 @@ type NodeValidatableResource struct {
} }
var ( var (
_ GraphNodeModuleInstance = (*NodeValidatableResource)(nil)
_ GraphNodeEvalable = (*NodeValidatableResource)(nil) _ GraphNodeEvalable = (*NodeValidatableResource)(nil)
_ GraphNodeReferenceable = (*NodeValidatableResource)(nil) _ GraphNodeReferenceable = (*NodeValidatableResource)(nil)
_ GraphNodeReferencer = (*NodeValidatableResource)(nil) _ GraphNodeReferencer = (*NodeValidatableResource)(nil)
@ -23,6 +25,12 @@ var (
_ GraphNodeAttachProviderMetaConfigs = (*NodeValidatableResource)(nil) _ GraphNodeAttachProviderMetaConfigs = (*NodeValidatableResource)(nil)
) )
func (n *NodeValidatableResource) Path() addrs.ModuleInstance {
// There is no expansion during validation, so we evaluate everything as
// single module instances.
return n.Addr.Module.UnkeyedInstanceShim()
}
// GraphNodeEvalable // GraphNodeEvalable
func (n *NodeValidatableResource) EvalTree() EvalNode { func (n *NodeValidatableResource) EvalTree() EvalNode {
addr := n.ResourceAddr() addr := n.ResourceAddr()

View File

@ -210,12 +210,12 @@ func mustResourceInstanceAddr(s string) addrs.AbsResourceInstance {
return addr return addr
} }
func mustResourceAddr(s string) addrs.AbsResource { func mustResourceAddr(s string) addrs.ConfigResource {
addr, diags := addrs.ParseAbsResourceStr(s) addr, diags := addrs.ParseAbsResourceStr(s)
if diags.HasErrors() { if diags.HasErrors() {
panic(diags.Err()) panic(diags.Err())
} }
return addr return addr.Config()
} }
func mustProviderConfig(s string) addrs.AbsProviderConfig { func mustProviderConfig(s string) addrs.AbsProviderConfig {

View File

@ -1,6 +1,9 @@
locals { locals {
val = 2 val = 2
bar = "baz" bar = "baz"
m = {
"a" = "b"
}
} }
variable "myvar" { variable "myvar" {
@ -8,21 +11,20 @@ variable "myvar" {
} }
module "child" { module "count_child" {
count = local.val count = local.val
foo = 2 foo = 2
bar = var.myvar bar = var.myvar
source = "./child" source = "./child"
} }
output "out" { module "for_each_child" {
value = module.child[*].out for_each = aws_instance.foo
foo = 2
bar = var.myvar
source = "./child"
} }
resource "aws_instance" "foo" { resource "aws_instance" "foo" {
num = 2 for_each = local.m
}
resource "aws_instance" "bar" {
foo = "${aws_instance.foo.num}"
} }

View File

@ -2,7 +2,6 @@ package terraform
import ( import (
"log" "log"
"sync"
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
@ -32,38 +31,14 @@ type ConfigTransformer struct {
// Mode will only add resources that match the given mode // Mode will only add resources that match the given mode
ModeFilter bool ModeFilter bool
Mode addrs.ResourceMode Mode addrs.ResourceMode
l sync.Mutex
uniqueMap map[string]struct{}
}
// FIXME: should we have an addr.Module + addr.Resource type?
type configName interface {
Name() string
} }
func (t *ConfigTransformer) Transform(g *Graph) error { func (t *ConfigTransformer) Transform(g *Graph) error {
// Lock since we use some internal state
t.l.Lock()
defer t.l.Unlock()
// If no configuration is available, we don't do anything // If no configuration is available, we don't do anything
if t.Config == nil { if t.Config == nil {
return nil return nil
} }
// Reset the uniqueness map. If we're tracking uniques, then populate
// it with addresses.
t.uniqueMap = make(map[string]struct{})
defer func() { t.uniqueMap = nil }()
if t.Unique {
for _, v := range g.Vertices() {
if rn, ok := v.(configName); ok {
t.uniqueMap[rn.Name()] = struct{}{}
}
}
}
// Start the transformation process // Start the transformation process
return t.transform(g, t.Config) return t.transform(g, t.Config)
} }
@ -117,12 +92,6 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er
}, },
} }
if _, ok := t.uniqueMap[abstract.Name()]; ok {
// We've already seen a resource with this address. This should
// never happen, because we enforce uniqueness in the config loader.
continue
}
var node dag.Vertex = abstract var node dag.Vertex = abstract
if f := t.Concrete; f != nil { if f := t.Concrete; f != nil {
node = f(abstract) node = f(abstract)

View File

@ -56,7 +56,7 @@ data.aws_ami.foo
func TestConfigTransformer_nonUnique(t *testing.T) { func TestConfigTransformer_nonUnique(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(NewNodeAbstractResource( g.Add(NewNodeAbstractResource(
addrs.RootModuleInstance.Resource( addrs.RootModule.Resource(
addrs.ManagedResourceMode, "aws_instance", "web", addrs.ManagedResourceMode, "aws_instance", "web",
), ),
)) ))
@ -78,33 +78,6 @@ openstack_floating_ip.random
} }
} }
func TestConfigTransformer_unique(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
g.Add(NewNodeAbstractResource(
addrs.RootModuleInstance.Resource(
addrs.ManagedResourceMode, "aws_instance", "web",
),
))
tf := &ConfigTransformer{
Config: testModule(t, "graph-basic"),
Unique: true,
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(`
aws_instance.web
aws_load_balancer.weblb
aws_security_group.firewall
openstack_floating_ip.random
`)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
const testConfigTransformerGraphBasicStr = ` const testConfigTransformerGraphBasicStr = `
aws_instance.web aws_instance.web
aws_load_balancer.weblb aws_load_balancer.weblb

View File

@ -94,7 +94,7 @@ func TestCBDEdgeTransformer(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -164,7 +164,7 @@ func TestCBDEdgeTransformerMulti(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"C","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"C","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
mustResourceAddr("test_object.A"), mustResourceAddr("test_object.A"),
mustResourceAddr("test_object.B"), mustResourceAddr("test_object.B"),
}, },
@ -234,7 +234,7 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -243,7 +243,7 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -320,7 +320,7 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -329,7 +329,7 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`), AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )

View File

@ -172,7 +172,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// destroyed along with its dependencies. // destroyed along with its dependencies.
func (t *DestroyEdgeTransformer) pruneResources(g *Graph) error { func (t *DestroyEdgeTransformer) pruneResources(g *Graph) error {
for _, v := range g.Vertices() { for _, v := range g.Vertices() {
n, ok := v.(*NodeApplyableResource) n, ok := v.(*nodeExpandApplyableResource)
if !ok { if !ok {
continue continue
} }

View File

@ -28,7 +28,7 @@ func TestDestroyEdgeTransformer_basic(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_string":"x"}`), AttrsJSON: []byte(`{"id":"B","test_string":"x"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -72,7 +72,7 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"B","test_string":"x"}`), AttrsJSON: []byte(`{"id":"B","test_string":"x"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_object.A")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("test_object.A")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -81,7 +81,7 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"C","test_string":"x"}`), AttrsJSON: []byte(`{"id":"C","test_string":"x"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
mustResourceAddr("test_object.A"), mustResourceAddr("test_object.A"),
mustResourceAddr("test_object.B"), mustResourceAddr("test_object.B"),
}, },
@ -138,7 +138,7 @@ func TestDestroyEdgeTransformer_module(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"a"}`), AttrsJSON: []byte(`{"id":"a"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("module.child.test_object.b")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("module.child.test_object.b")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -191,7 +191,7 @@ func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b","test_string":"x"}`), AttrsJSON: []byte(`{"id":"b","test_string":"x"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("module.child.test_object.a")}, Dependencies: []addrs.ConfigResource{mustResourceAddr("module.child.test_object.a")},
}, },
mustProviderConfig(`provider["registry.terraform.io/-/test"]`), mustProviderConfig(`provider["registry.terraform.io/-/test"]`),
) )
@ -200,7 +200,7 @@ func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{ &states.ResourceInstanceObjectSrc{
Status: states.ObjectReady, Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"c","test_string":"x"}`), AttrsJSON: []byte(`{"id":"c","test_string":"x"}`),
Dependencies: []addrs.AbsResource{ Dependencies: []addrs.ConfigResource{
mustResourceAddr("module.child.test_object.a"), mustResourceAddr("module.child.test_object.a"),
mustResourceAddr("module.child.test_object.b"), mustResourceAddr("module.child.test_object.b"),
}, },
@ -237,12 +237,7 @@ module.child.test_object.c (destroy)
func testDestroyNode(addrString string) GraphNodeDestroyer { func testDestroyNode(addrString string) GraphNodeDestroyer {
instAddr := mustResourceInstanceAddr(addrString) instAddr := mustResourceInstanceAddr(addrString)
abs := NewNodeAbstractResource(instAddr.ContainingResource()) inst := NewNodeAbstractResourceInstance(instAddr)
inst := &NodeAbstractResourceInstance{
NodeAbstractResource: *abs,
InstanceKey: instAddr.Resource.Key,
}
return &NodeDestroyResourceInstance{NodeAbstractResourceInstance: inst} return &NodeDestroyResourceInstance{NodeAbstractResourceInstance: inst}
} }

View File

@ -18,18 +18,17 @@ import (
type OrphanResourceCountTransformer struct { type OrphanResourceCountTransformer struct {
Concrete ConcreteResourceInstanceNodeFunc Concrete ConcreteResourceInstanceNodeFunc
Addr addrs.ConfigResource // Addr of the resource to look for orphans Addr addrs.AbsResource // Addr of the resource to look for orphans
InstanceAddrs []addrs.AbsResourceInstance // Addresses that currently exist in config InstanceAddrs []addrs.AbsResourceInstance // Addresses that currently exist in config
State *states.State // Full global state State *states.State // Full global state
} }
func (t *OrphanResourceCountTransformer) Transform(g *Graph) error { func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
resources := t.State.Resources(t.Addr) rs := t.State.Resource(t.Addr)
if len(resources) == 0 { if rs == nil {
return nil // Resource doesn't exist in state, so nothing to do! return nil // Resource doesn't exist in state, so nothing to do!
} }
for _, rs := range resources {
// This is an O(n*m) analysis, which we accept for now because the // This is an O(n*m) analysis, which we accept for now because the
// number of instances of a single resource ought to always be small in any // number of instances of a single resource ought to always be small in any
// reasonable Terraform configuration. // reasonable Terraform configuration.
@ -51,7 +50,6 @@ func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
log.Printf("[TRACE] OrphanResourceCountTransformer: adding %s as %T", thisAddr, node) log.Printf("[TRACE] OrphanResourceCountTransformer: adding %s as %T", thisAddr, node)
g.Add(node) g.Add(node)
} }
}
return nil return nil
} }

View File

@ -160,7 +160,7 @@ func (t *OrphanResourceTransformer) Transform(g *Graph) error {
} }
addr := rs.Addr addr := rs.Addr
abstract := NewNodeAbstractResource(addr) abstract := NewNodeAbstractResource(addr.Config())
var node dag.Vertex = abstract var node dag.Vertex = abstract
if f := t.Concrete; f != nil { if f := t.Concrete; f != nil {
node = f(abstract) node = f(abstract)

View File

@ -79,8 +79,7 @@ func (t *DestroyOutputTransformer) Transform(g *Graph) error {
// create the destroy node for this output // create the destroy node for this output
node := &NodeDestroyableOutput{ node := &NodeDestroyableOutput{
Addr: output.Addr, Addr: output.Addr.Absolute(addrs.RootModuleInstance),
Module: output.Module,
Config: output.Config, Config: output.Config,
} }

View File

@ -173,7 +173,7 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
p := req.Addr p := req.Addr
target := m[key] target := m[key]
_, ok := v.(GraphNodeModuleInstance) _, ok := v.(GraphNodeModulePath)
if !ok && target == nil { if !ok && target == nil {
// No target and no path to traverse up from // No target and no path to traverse up from
diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p)) diags = diags.Append(fmt.Errorf("%s: provider %s couldn't be found", dag.VertexName(v), p))
@ -470,11 +470,6 @@ func (n *graphNodeCloseProvider) Name() string {
return n.Addr.String() + " (close)" return n.Addr.String() + " (close)"
} }
// GraphNodeModuleInstance impl.
func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance {
return n.Addr.Module.UnkeyedInstanceShim()
}
// GraphNodeModulePath // GraphNodeModulePath
func (n *graphNodeCloseProvider) ModulePath() addrs.Module { func (n *graphNodeCloseProvider) ModulePath() addrs.Module {
return n.Addr.Module return n.Addr.Module
@ -534,10 +529,6 @@ func (n *graphNodeProxyProvider) ProviderAddr() addrs.AbsProviderConfig {
return n.addr return n.addr
} }
func (n *graphNodeProxyProvider) Path() addrs.ModuleInstance {
return n.addr.Module.UnkeyedInstanceShim()
}
func (n *graphNodeProxyProvider) ModulePath() addrs.Module { func (n *graphNodeProxyProvider) ModulePath() addrs.Module {
return n.addr.Module return n.addr.Module
} }