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),
&states.ResourceInstanceObjectSrc{
AttrsJSON: []byte(`{"id":"i-abc123"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("test_instance.foo")},
Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
Status: states.ObjectReady,
},
addrs.AbsProviderConfig{

View File

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

View File

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

View File

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

View File

@ -20,8 +20,6 @@ func TestLoadModuleCall(t *testing.T) {
file, diags := parser.LoadConfigFile("module-calls.tf")
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.`,
})

View File

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

View File

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

View File

@ -153,9 +153,9 @@ func (obj *ResourceInstanceObjectSrc) DeepCopy() *ResourceInstanceObjectSrc {
// Some addrs.Referencable implementations are technically mutable, but
// 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 {
dependencies = make([]addrs.AbsResource, len(obj.Dependencies))
dependencies = make([]addrs.ConfigResource, len(obj.Dependencies))
copy(dependencies, obj.Dependencies)
}
@ -198,9 +198,9 @@ func (obj *ResourceInstanceObject) DeepCopy() *ResourceInstanceObject {
// Some addrs.Referenceable implementations are technically mutable, but
// 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 {
dependencies = make([]addrs.AbsResource, len(obj.Dependencies))
dependencies = make([]addrs.ConfigResource, len(obj.Dependencies))
copy(dependencies, obj.Dependencies)
}

View File

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

View File

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

View File

@ -298,7 +298,7 @@ func TestContext2Apply_resourceDependsOnModuleStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -1273,7 +1273,7 @@ func testContext2Apply_destroyDependsOn(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{mustResourceAddr("aws_instance.bar")},
Dependencies: []addrs.ConfigResource{mustResourceAddr("aws_instance.bar")},
},
mustProviderConfig(`provider["registry.terraform.io/-/aws"]`),
)
@ -1329,7 +1329,7 @@ func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{},
Dependencies: []addrs.ConfigResource{},
},
addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"),
@ -1345,14 +1345,14 @@ func TestContext2Apply_destroyDependsOnStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Module: root.Addr,
Module: root.Addr.Module(),
},
},
},
@ -1427,7 +1427,7 @@ func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{},
Dependencies: []addrs.ConfigResource{},
},
addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"),
@ -1443,14 +1443,14 @@ func TestContext2Apply_destroyDependsOnStateOnlyModule(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"bar"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Module: child.Addr,
Module: child.Addr.Module(),
},
},
},
@ -2708,7 +2708,7 @@ func TestContext2Apply_moduleDestroyOrder(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -3170,8 +3170,8 @@ func TestContext2Apply_moduleProviderAliasTargets(t *testing.T) {
},
),
Targets: []addrs.Targetable{
addrs.AbsResource{
Module: addrs.RootModuleInstance,
addrs.ConfigResource{
Module: addrs.RootModule,
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "nonexistent",
@ -8025,7 +8025,7 @@ func TestContext2Apply_targetedDestroyCountDeps(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -8631,14 +8631,14 @@ func TestContext2Apply_createBefore_depends(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "web",
},
Module: addrs.RootModuleInstance,
Module: addrs.RootModule,
},
},
},
@ -8764,14 +8764,14 @@ func TestContext2Apply_singleDestroy(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"baz","instance":"bar"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "web",
},
Module: addrs.RootModuleInstance,
Module: addrs.RootModule,
},
},
},
@ -10639,22 +10639,22 @@ func TestContext2Apply_cbdCycle(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"a","require_new":"old","foo":"b"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_instance",
Name: "b",
},
Module: addrs.RootModuleInstance,
Module: addrs.RootModule,
},
addrs.AbsResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_instance",
Name: "c",
},
Module: addrs.RootModuleInstance,
Module: addrs.RootModule,
},
},
},
@ -10672,14 +10672,14 @@ func TestContext2Apply_cbdCycle(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b","require_new":"old","foo":"c"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_instance",
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)
}
}
func TestContext2Plan_moduleCount(t *testing.T) {
// This test is skipped with count disabled.
t.Skip()
//FIXME: add for_each and single modules to this test
m := testModule(t, "plan-modules-count")
func TestContext2Plan_moduleExpand(t *testing.T) {
// Test a smattering of plan expansion behavior
m := testModule(t, "plan-modules-expand")
p := testProvider("aws")
p.DiffFn = testDiffFn
ctx := testContext2(t, &ContextOpts{
@ -451,31 +448,18 @@ func TestContext2Plan_moduleCount(t *testing.T) {
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"]
ty := schema.ImpliedType()
expectFoo := objectVal(t, schema, map[string]cty.Value{
"id": cty.UnknownVal(cty.String),
"foo": cty.StringVal("2"),
"type": cty.StringVal("aws_instance")},
)
expectNum := objectVal(t, schema, map[string]cty.Value{
"id": cty.UnknownVal(cty.String),
"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"),
})
expected := map[string]struct{}{
`aws_instance.foo["a"]`: struct{}{},
`module.count_child[1].aws_instance.foo[0]`: struct{}{},
`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{}{},
`module.for_each_child["a"].aws_instance.foo[1]`: struct{}{},
`module.for_each_child["a"].aws_instance.foo[0]`: struct{}{},
}
for _, res := range plan.Changes.Resources {
if res.Action != plans.Create {
@ -486,22 +470,14 @@ func TestContext2Plan_moduleCount(t *testing.T) {
t.Fatal(err)
}
var expected cty.Value
switch i := ric.Addr.String(); i {
case "aws_instance.bar":
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)
_, ok := expected[ric.Addr.String()]
if !ok {
t.Fatal("unexpected resource:", ric.Addr.String())
}
checkVals(t, expected, ric.After)
delete(expected, ric.Addr.String())
}
for addr := range expected {
t.Error("missing resource", addr)
}
}

View File

@ -1985,10 +1985,10 @@ func TestRefresh_updateDependencies(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"foo"}`),
Dependencies: []addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
// Existing dependencies should not be removed during refresh
{
Module: addrs.RootModuleInstance,
Module: addrs.RootModule,
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
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
// signal something normal such as EvalEarlyExitError.
func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) {
path := "unknown"
if ctx != nil {
path = ctx.Path().String()
}
if path == "" {
path = "<root>"
}
log.Printf("[TRACE] %s: eval: %T", path, n)
log.Printf("[TRACE] eval: %T", n)
output, err := n.Eval(ctx)
if err != nil {
switch err.(type) {
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:
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:
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
// EvalContext objects for a given configuration.
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 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
// eval context.
Evaluator *Evaluator
@ -70,6 +77,13 @@ type BuiltinEvalContext struct {
// BuiltinEvalContext implements EvalContext
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{} {
// This can happen during tests. During tests, we just block forever.
if ctx.StopContext == nil {
@ -104,12 +118,6 @@ func (ctx *BuiltinEvalContext) Input() UIInput {
}
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 p := ctx.Provider(addr); p != nil {
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 {
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()
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) {
absProvider := pc
if !absProvider.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", absProvider, ctx.Path()))
}
if !ctx.Path().IsRoot() {
if !pc.Module.IsRoot() {
// Only root module provider configurations can have input.
log.Printf("[WARN] BuiltinEvalContext: attempt to SetProviderInput for non-root module")
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 {
if !ctx.pathSet {
panic("context path not set")
}
data := &evaluationStateData{
Evaluator: ctx.Evaluator,
ModulePath: ctx.PathValue,
@ -301,6 +300,9 @@ func (ctx *BuiltinEvalContext) EvaluationScope(self addrs.Referenceable, keyData
}
func (ctx *BuiltinEvalContext) Path() addrs.ModuleInstance {
if !ctx.pathSet {
panic("context path not set")
}
return ctx.PathValue
}
@ -308,6 +310,10 @@ func (ctx *BuiltinEvalContext) SetModuleCallArguments(n addrs.ModuleCallInstance
ctx.VariableValuesLock.Lock()
defer ctx.VariableValuesLock.Unlock()
if !ctx.pathSet {
panic("context path not set")
}
childPath := n.ModuleInstance(ctx.PathValue)
key := childPath.String()

View File

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

View File

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

View File

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

View File

@ -205,7 +205,7 @@ type EvalWriteState struct {
// Dependencies are the inter-resource dependencies to be stored in the
// state.
Dependencies *[]addrs.AbsResource
Dependencies *[]addrs.ConfigResource
}
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
// list rather than as not set at all.
type EvalWriteResourceState struct {
Addr addrs.ConfigResource
Addr addrs.AbsResource
Config *configs.Resource
ProviderAddr addrs.AbsProviderConfig
}
// TODO: test
func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) {
var diags tfdiags.Diagnostics
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)
diags = diags.Append(countDiags)
if countDiags.HasErrors() {
@ -482,25 +486,17 @@ func (n *EvalWriteResourceState) Eval(ctx EvalContext) (interface{}, error) {
if forEach != nil {
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
// 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 {
case states.EachList:
expander.SetResourceCount(module, n.Addr.Resource, count)
expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
case states.EachMap:
expander.SetResourceForEach(module, n.Addr.Resource, forEach)
expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEach)
default:
expander.SetResourceSingle(module, n.Addr.Resource)
}
expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
}
return nil, nil
@ -542,7 +538,7 @@ type EvalRefreshDependencies struct {
// Prior State
State **states.ResourceInstanceObject
// Dependencies to write to the new state
Dependencies *[]addrs.AbsResource
Dependencies *[]addrs.ConfigResource
}
func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
@ -552,7 +548,7 @@ func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil
}
depMap := make(map[string]addrs.AbsResource)
depMap := make(map[string]addrs.ConfigResource)
for _, d := range *n.Dependencies {
depMap[d.String()] = d
}
@ -566,7 +562,7 @@ func (n *EvalRefreshDependencies) Eval(ctx EvalContext) (interface{}, error) {
return nil, nil
}
deps := make([]addrs.AbsResource, 0, len(depMap))
deps := make([]addrs.ConfigResource, 0, len(depMap))
for _, d := range depMap {
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 {
// The callbacks for enter/exiting a graph
ctx := walker.EnterPath(g.Path)
defer walker.ExitPath(g.Path)
ctx := walker.EvalContext()
// Walk the graph.
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
// with a GraphNodeModuleInstance impl.
vertexCtx := ctx
if pn, ok := v.(GraphNodeModuleInstance); ok && len(pn.Path()) > 0 {
if pn, ok := v.(GraphNodeModuleInstance); ok {
vertexCtx = walker.EnterPath(pn.Path())
defer walker.ExitPath(pn.Path())
}

View File

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

View File

@ -104,7 +104,7 @@ func TestApplyGraphBuilder_depCbd(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -209,7 +209,7 @@ func TestApplyGraphBuilder_doubleCBD(t *testing.T) {
continue
}
switch tv.Addr.Resource.Name {
switch tv.Addr.Resource.Resource.Name {
case "A":
destroyA = fmt.Sprintf("test_object.A (destroy deposed %s)", tv.DeposedKey)
case "B":
@ -273,7 +273,7 @@ func TestApplyGraphBuilder_destroyStateOnly(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -378,7 +378,7 @@ func TestApplyGraphBuilder_moduleDestroy(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -566,14 +566,14 @@ func TestApplyGraphBuilder_updateFromOrphan(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
Name: "a",
},
Module: root.Addr,
Module: root.Addr.Module(),
},
},
},
@ -670,14 +670,14 @@ func TestApplyGraphBuilder_updateFromCBDOrphan(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`),
Dependencies: []addrs.AbsResource{
addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
addrs.ConfigResource{
Resource: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "test_object",
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 {
return &NodePlannableResource{
return &nodeExpandPlannableResource{
NodeAbstractResource: a,
}
}

View File

@ -67,7 +67,7 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
}
concreteManagedResource := func(a *NodeAbstractResource) dag.Vertex {
return &NodeRefreshableManagedResource{
return &nodeExpandRefreshableManagedResource{
NodeAbstractResource: a,
}
}
@ -87,7 +87,7 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
}
concreteDataResource := func(a *NodeAbstractResource) dag.Vertex {
return &NodeRefreshableDataResource{
return &nodeExpandRefreshableDataResource{
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[2] - *terraform.NodeRefreshableManagedResourceInstance
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[1] (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
test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
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
// with Graph.Walk will invoke the given callbacks under certain events.
type GraphWalker interface {
EvalContext() EvalContext
EnterPath(addrs.ModuleInstance) EvalContext
ExitPath(addrs.ModuleInstance)
EnterVertex(dag.Vertex)
@ -22,6 +23,7 @@ type GraphWalker interface {
// implementing all the required functions.
type NullGraphWalker struct{}
func (NullGraphWalker) EvalContext() EvalContext { return new(MockEvalContext) }
func (NullGraphWalker) EnterPath(addrs.ModuleInstance) EvalContext { return new(MockEvalContext) }
func (NullGraphWalker) ExitPath(addrs.ModuleInstance) {}
func (NullGraphWalker) EnterVertex(dag.Vertex) {}

View File

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

View File

@ -10,12 +10,45 @@ import (
"github.com/zclconf/go-cty/cty"
)
// NodeRefreshableDataResource represents a resource that is "refreshable".
type NodeRefreshableDataResource struct {
type nodeExpandRefreshableDataResource struct {
*NodeAbstractResource
}
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)
_ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil)
_ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil)
@ -24,6 +57,10 @@ var (
_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil)
)
func (n *NodeRefreshableDataResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
// GraphNodeDynamicExpandable
func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
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.
fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1)
var instanceAddrs []addrs.AbsResourceInstance
// Inform our instance expander about our expansion results above,
// and then use it to calculate the instance addresses we'll expand for.
expander := ctx.InstanceExpander()
for _, path := range expander.ExpandModule(n.Addr.Module) {
switch {
case count >= 0:
expander.SetResourceCount(path, n.ResourceAddr().Resource, count)
expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
case forEachMap != nil:
expander.SetResourceForEach(path, n.ResourceAddr().Resource, forEachMap)
expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachMap)
default:
expander.SetResourceSingle(path, n.ResourceAddr().Resource)
}
instanceAddrs = append(instanceAddrs, expander.ExpandResource(n.ResourceAddr().Absolute(path))...)
expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
}
instanceAddrs := expander.ExpandResource(n.Addr)
// Our graph transformers require access to the full state, so we'll
// temporarily lock it while we work on this.
@ -114,7 +147,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er
// directly as NodeDestroyableDataResource.
&OrphanResourceCountTransformer{
Concrete: concreteResourceDestroyable,
Addr: n.ResourceAddr(),
Addr: n.Addr,
InstanceAddrs: instanceAddrs,
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{
NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo"),
Addr: addr,
Config: m.Module.DataResources["data.aws_instance.foo"],
},
Addr: addr.Absolute(addrs.RootModuleInstance),
}
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{
NodeAbstractResource: &NodeAbstractResource{
Addr: addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo"),
Addr: addr,
Config: m.Module.DataResources["data.aws_instance.foo"],
ResolvedProvider: addrs.AbsProviderConfig{
Provider: addrs.NewLegacyProvider("aws"),
Module: addrs.RootModule,
},
},
Addr: addr.Absolute(addrs.RootModuleInstance),
}
g, err := n.DynamicExpand(&MockEvalContext{

View File

@ -92,6 +92,11 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
_, 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)
if countDiags.HasErrors() {
return nil, countDiags.Err()
@ -110,17 +115,13 @@ func (n *evalPrepareModuleExpansion) Eval(ctx EvalContext) (interface{}, error)
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 {
case states.EachList:
expander.SetModuleCount(path, call, count)
expander.SetModuleCount(module, call, count)
case states.EachMap:
expander.SetModuleForEach(path, call, forEach)
expander.SetModuleForEach(module, call, forEach)
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":
// its application will remove the output from the state.
type NodeDestroyableOutput struct {
Addr addrs.OutputValue
Module addrs.Module
Addr addrs.AbsOutputValue
Config *configs.Output // Config is the output in the config
}
@ -254,7 +253,7 @@ func (n *NodeDestroyableOutput) Name() string {
// GraphNodeModulePath
func (n *NodeDestroyableOutput) ModulePath() addrs.Module {
return n.Module
return n.Addr.Module.Module()
}
// RemovableIfNotTargeted

View File

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

View File

@ -8,6 +8,44 @@ import (
"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":
// 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.
type NodeApplyableResource struct {
*NodeAbstractResource
Addr addrs.AbsResource
}
var (
_ GraphNodeModuleInstance = (*NodeApplyableResource)(nil)
_ GraphNodeConfigResource = (*NodeApplyableResource)(nil)
_ GraphNodeEvalable = (*NodeApplyableResource)(nil)
_ GraphNodeProviderConsumer = (*NodeApplyableResource)(nil)
@ -28,8 +69,8 @@ var (
_ GraphNodeReferencer = (*NodeApplyableResource)(nil)
)
func (n *NodeApplyableResource) Name() string {
return n.NodeAbstractResource.Name() + " (prepare state)"
func (n *NodeApplyableResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
func (n *NodeApplyableResource) References() []*addrs.Reference {

View File

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

View File

@ -26,6 +26,7 @@ type NodeDestroyResourceInstance struct {
}
var (
_ GraphNodeModuleInstance = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeConfigResource = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeResourceInstance = (*NodeDestroyResourceInstance)(nil)
_ GraphNodeDestroyer = (*NodeDestroyResourceInstance)(nil)
@ -63,7 +64,7 @@ func (n *NodeDestroyResourceInstance) CreateBeforeDestroy() bool {
// Otherwise check the state for a stored destroy order
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 {
return s.Current.CreateBeforeDestroy
}
@ -136,7 +137,7 @@ func (n *NodeDestroyResourceInstance) EvalTree() EvalNode {
rs := n.ResourceState
var is *states.ResourceInstance
if rs != nil {
is = rs.Instance(n.InstanceKey)
is = rs.Instance(n.Addr.Resource.Key)
}
if is == nil {
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
// instances being destroyed that affects remote objects. During graph
@ -292,6 +293,7 @@ type NodeDestroyResource struct {
}
var (
_ GraphNodeModuleInstance = (*NodeDestroyResource)(nil)
_ GraphNodeConfigResource = (*NodeDestroyResource)(nil)
_ GraphNodeReferenceable = (*NodeDestroyResource)(nil)
_ GraphNodeReferencer = (*NodeDestroyResource)(nil)
@ -305,6 +307,10 @@ var (
_ GraphNodeNoProvider = (*NodeDestroyResource)(nil)
)
func (n *NodeDestroyResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
func (n *NodeDestroyResource) Name() string {
return n.ResourceAddr().String() + " (clean up state)"
}

View File

@ -3,13 +3,16 @@ package terraform
import (
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/dag"
"github.com/hashicorp/terraform/tfdiags"
)
// NodePlannableResource represents a resource that is "plannable":
// it is ready to be planned in order to create a diff.
type NodePlannableResource struct {
// nodeExpandPlannableResource handles the first layer of resource
// expansion. 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 nodeExpandPlannableResource struct {
*NodeAbstractResource
// ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD
@ -19,6 +22,64 @@ type NodePlannableResource struct {
}
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)
_ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil)
_ GraphNodeReferenceable = (*NodePlannableResource)(nil)
@ -27,6 +88,19 @@ var (
_ 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
func (n *NodePlannableResource) EvalTree() EvalNode {
if n.Config == nil {
@ -67,26 +141,16 @@ func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error {
func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
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
// expansion of this resource and of all of its containing modules, so
// it can tell us which instance addresses we need to process.
expander := ctx.InstanceExpander()
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
// temporarily lock it while we work on this.
state := ctx.State().Lock()
@ -138,7 +202,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
// Add the count/for_each orphans
&OrphanResourceCountTransformer{
Concrete: concreteResourceOrphan,
Addr: n.ResourceAddr(),
Addr: n.Addr,
InstanceAddrs: instanceAddrs,
State: state,
},

View File

@ -14,33 +14,75 @@ import (
"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
// NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
type NodeRefreshableManagedResource struct {
*NodeAbstractResource
Addr addrs.AbsResource
// We attach dependencies to the Resource during refresh, since the
// instances are instantiated during DynamicExpand.
Dependencies []addrs.AbsResource
Dependencies []addrs.ConfigResource
}
var (
_ GraphNodeModuleInstance = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeDynamicExpandable = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeReferenceable = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeReferencer = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeConfigResource = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResource)(nil)
_ GraphNodeAttachDependencies = (*NodeRefreshableManagedResource)(nil)
)
// GraphNodeAttachDependencies
func (n *NodeRefreshableManagedResource) AttachDependencies(deps []addrs.ConfigResource) {
var shimmed []addrs.AbsResource
for _, r := range deps {
shimmed = append(shimmed, r.Absolute(r.Module.UnkeyedInstanceShim()))
}
n.Dependencies = shimmed
func (n *NodeRefreshableManagedResource) Path() addrs.ModuleInstance {
return n.Addr.Module
}
// GraphNodeDynamicExpandable
@ -60,22 +102,20 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
// Next we need to potentially rename an instance address in the state
// 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,
// and then use it to calculate the instance addresses we'll expand for.
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Addr.Module) {
switch {
case count >= 0:
expander.SetResourceCount(module, n.ResourceAddr().Resource, count)
expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
case forEachMap != nil:
expander.SetResourceForEach(module, n.ResourceAddr().Resource, forEachMap)
expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachMap)
default:
expander.SetResourceSingle(module, n.ResourceAddr().Resource)
expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
}
}
instanceAddrs := expander.ExpandModuleResource(n.Addr.Module, n.ResourceAddr().Resource)
instanceAddrs := expander.ExpandResource(n.Addr)
// Our graph transformers require access to the full state, so we'll
// temporarily lock it while we work on this.
@ -101,7 +141,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
&ResourceCountTransformer{
Concrete: concreteResource,
Schema: n.Schema,
Addr: n.ResourceAddr(),
Addr: n.Addr.Config(),
InstanceAddrs: instanceAddrs,
},
@ -109,7 +149,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph,
// during a scale in.
&OrphanResourceCountTransformer{
Concrete: concreteResource,
Addr: n.ResourceAddr(),
Addr: n.Addr,
InstanceAddrs: instanceAddrs,
State: state,
},

View File

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

View File

@ -1,6 +1,7 @@
package terraform
import (
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
"github.com/hashicorp/terraform/configs/configschema"
"github.com/hashicorp/terraform/providers"
@ -15,6 +16,7 @@ type NodeValidatableResource struct {
}
var (
_ GraphNodeModuleInstance = (*NodeValidatableResource)(nil)
_ GraphNodeEvalable = (*NodeValidatableResource)(nil)
_ GraphNodeReferenceable = (*NodeValidatableResource)(nil)
_ GraphNodeReferencer = (*NodeValidatableResource)(nil)
@ -23,6 +25,12 @@ var (
_ 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
func (n *NodeValidatableResource) EvalTree() EvalNode {
addr := n.ResourceAddr()

View File

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

View File

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

View File

@ -2,7 +2,6 @@ package terraform
import (
"log"
"sync"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
@ -32,38 +31,14 @@ type ConfigTransformer struct {
// Mode will only add resources that match the given mode
ModeFilter bool
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 {
// 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 t.Config == 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
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
if f := t.Concrete; f != nil {
node = f(abstract)

View File

@ -56,7 +56,7 @@ data.aws_ami.foo
func TestConfigTransformer_nonUnique(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
g.Add(NewNodeAbstractResource(
addrs.RootModuleInstance.Resource(
addrs.RootModule.Resource(
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 = `
aws_instance.web
aws_load_balancer.weblb

View File

@ -94,7 +94,7 @@ func TestCBDEdgeTransformer(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -164,7 +164,7 @@ func TestCBDEdgeTransformerMulti(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{"id":"C","test_list":["x"]}`),
Dependencies: []addrs.AbsResource{
Dependencies: []addrs.ConfigResource{
mustResourceAddr("test_object.A"),
mustResourceAddr("test_object.B"),
},
@ -234,7 +234,7 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -243,7 +243,7 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -320,7 +320,7 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)
@ -329,7 +329,7 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
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"]`),
)

View File

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

View File

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

View File

@ -18,22 +18,21 @@ import (
type OrphanResourceCountTransformer struct {
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
State *states.State // Full global state
}
func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
resources := t.State.Resources(t.Addr)
if len(resources) == 0 {
rs := t.State.Resource(t.Addr)
if rs == nil {
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
// number of instances of a single resource ought to always be small in any
// reasonable Terraform configuration.
Have:
Have:
for key := range rs.Instances {
thisAddr := rs.Addr.Instance(key)
for _, wantAddr := range t.InstanceAddrs {
@ -51,7 +50,6 @@ func (t *OrphanResourceCountTransformer) Transform(g *Graph) error {
log.Printf("[TRACE] OrphanResourceCountTransformer: adding %s as %T", thisAddr, node)
g.Add(node)
}
}
return nil
}

View File

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

View File

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

View File

@ -173,7 +173,7 @@ func (t *ProviderTransformer) Transform(g *Graph) error {
p := req.Addr
target := m[key]
_, ok := v.(GraphNodeModuleInstance)
_, ok := v.(GraphNodeModulePath)
if !ok && target == nil {
// 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))
@ -470,11 +470,6 @@ func (n *graphNodeCloseProvider) Name() string {
return n.Addr.String() + " (close)"
}
// GraphNodeModuleInstance impl.
func (n *graphNodeCloseProvider) Path() addrs.ModuleInstance {
return n.Addr.Module.UnkeyedInstanceShim()
}
// GraphNodeModulePath
func (n *graphNodeCloseProvider) ModulePath() addrs.Module {
return n.Addr.Module
@ -534,10 +529,6 @@ func (n *graphNodeProxyProvider) ProviderAddr() addrs.AbsProviderConfig {
return n.addr
}
func (n *graphNodeProxyProvider) Path() addrs.ModuleInstance {
return n.addr.Module.UnkeyedInstanceShim()
}
func (n *graphNodeProxyProvider) ModulePath() addrs.Module {
return n.addr.Module
}