diff --git a/terraform/context_eval_test.go b/terraform/context_eval_test.go index 145ef1372..8f7035d01 100644 --- a/terraform/context_eval_test.go +++ b/terraform/context_eval_test.go @@ -38,7 +38,7 @@ func TestContextEval(t *testing.T) { }, { `module.child.result`, - cty.UnknownVal(cty.Number), + cty.NumberIntVal(6), false, }, } diff --git a/terraform/graph_builder_apply.go b/terraform/graph_builder_apply.go index b4e573142..0ca7cf543 100644 --- a/terraform/graph_builder_apply.go +++ b/terraform/graph_builder_apply.go @@ -87,6 +87,12 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { Config: b.Config, }, + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, + // Creates all the resource instances represented in the diff, along // with dependency edges against the whole-resource nodes added by // ConfigTransformer above. @@ -96,31 +102,19 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { Changes: b.Changes, }, + // Attach the state + &AttachStateTransformer{State: b.State}, + // Create orphan output nodes &OrphanOutputTransformer{Config: b.Config, State: b.State}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Attach the state - &AttachStateTransformer{State: b.State}, - // Provisioner-related transformations &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, &ProvisionerTransformer{}, - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - // add providers TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), @@ -150,7 +144,6 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { State: b.State, Schemas: b.Schemas, }, - &CBDEdgeTransformer{ Config: b.Config, State: b.State, diff --git a/terraform/graph_builder_destroy_plan.go b/terraform/graph_builder_destroy_plan.go index 94cc90d6c..cbd0c12fc 100644 --- a/terraform/graph_builder_destroy_plan.go +++ b/terraform/graph_builder_destroy_plan.go @@ -92,8 +92,13 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer { // created proper destroy ordering. &TargetsTransformer{Targets: b.Targets}, + // Close opened plugin connections + &CloseProviderTransformer{}, + // Close the root module &CloseRootModuleTransformer{}, + + &TransitiveReductionTransformer{}, } return steps diff --git a/terraform/graph_builder_eval.go b/terraform/graph_builder_eval.go index 31aaf0cfc..5c01f6fc9 100644 --- a/terraform/graph_builder_eval.go +++ b/terraform/graph_builder_eval.go @@ -60,28 +60,20 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer { // Creates all the data resources that aren't in the state. This will also // add any orphans from scaling in as destroy nodes. &ConfigTransformer{ - Concrete: nil, // just use the abstract type - Config: b.Config, - Unique: true, + Config: b.Config, }, - // Attach the state - &AttachStateTransformer{State: b.State}, + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Add root variables - &RootVariableTransformer{Config: b.Config}, - - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, + // Attach the state + &AttachStateTransformer{State: b.State}, TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), @@ -92,9 +84,7 @@ func (b *EvalGraphBuilder) Steps() []GraphTransformer { // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. - &ModuleExpansionTransformer{ - Config: b.Config, - }, + &ModuleExpansionTransformer{Config: b.Config}, // Connect so that the references are ready for targeting. We'll // have to connect again later for providers and so on. diff --git a/terraform/graph_builder_import.go b/terraform/graph_builder_import.go index 872478119..8754f3ebc 100644 --- a/terraform/graph_builder_import.go +++ b/terraform/graph_builder_import.go @@ -55,26 +55,20 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer { // Create all our resources from the configuration and state &ConfigTransformer{Config: config}, + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, + &LocalTransformer{Config: b.Config}, + &OutputTransformer{Config: b.Config}, + // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, // Add the import steps &ImportStateTransformer{Targets: b.ImportTargets, Config: b.Config}, - // Add root variables - &RootVariableTransformer{Config: b.Config}, - TransformProviders(b.Components.ResourceProviders(), concreteProvider, config), - // Add the local values - &LocalTransformer{Config: b.Config}, - - // Add the outputs - &OutputTransformer{Config: b.Config}, - - // Add module variables - &ModuleVariableTransformer{Config: b.Config}, - // Must attach schemas before ReferenceTransformer so that we can // analyze the configuration to find references. &AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config}, @@ -82,9 +76,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer { // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. - &ModuleExpansionTransformer{ - Config: b.Config, - }, + &ModuleExpansionTransformer{Config: b.Config}, // Connect so that the references are ready for targeting. We'll // have to connect again later for providers and so on. diff --git a/terraform/graph_builder_plan.go b/terraform/graph_builder_plan.go index d6bab3a6d..7e19e1343 100644 --- a/terraform/graph_builder_plan.go +++ b/terraform/graph_builder_plan.go @@ -84,10 +84,10 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { Config: b.Config, }, - // Add the local values + // Add dynamic values + &RootVariableTransformer{Config: b.Config}, + &ModuleVariableTransformer{Config: b.Config}, &LocalTransformer{Config: b.Config}, - - // Add the outputs &OutputTransformer{Config: b.Config}, // Add orphan resources @@ -106,29 +106,20 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { State: b.State, }, + // Attach the state + &AttachStateTransformer{State: b.State}, + // Create orphan output nodes - &OrphanOutputTransformer{ - Config: b.Config, - State: b.State, - }, + &OrphanOutputTransformer{Config: b.Config, State: b.State}, // Attach the configuration to any resources &AttachResourceConfigTransformer{Config: b.Config}, - // Attach the state - &AttachStateTransformer{State: b.State}, - - // Add root variables - &RootVariableTransformer{Config: b.Config}, - + // Provisioner-related transformations &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, &ProvisionerTransformer{}, - // Add module variables - &ModuleVariableTransformer{ - Config: b.Config, - }, - + // add providers TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config), // Remove modules no longer present in the config @@ -141,10 +132,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Create expansion nodes for all of the module calls. This must // come after all other transformers that create nodes representing // objects that can belong to modules. - &ModuleExpansionTransformer{ - Concrete: b.ConcreteModule, - Config: b.Config, - }, + &ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config}, // Connect so that the references are ready for targeting. We'll // have to connect again later for providers and so on. @@ -156,9 +144,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { &attachDataResourceDependenciesTransformer{}, // Target - &TargetsTransformer{ - Targets: b.Targets, - }, + &TargetsTransformer{Targets: b.Targets}, // Detect when create_before_destroy must be forced on for a particular // node due to dependency edges, to avoid graph cycles during apply. @@ -171,7 +157,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Close opened plugin connections &CloseProviderTransformer{}, - &CloseProvisionerTransformer{}, // Close the root module &CloseRootModuleTransformer{}, diff --git a/terraform/node_module_variable.go b/terraform/node_module_variable.go index b635685d1..78ddb6e8f 100644 --- a/terraform/node_module_variable.go +++ b/terraform/node_module_variable.go @@ -153,13 +153,13 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) error { var err error switch op { - case walkPlan, walkApply, walkDestroy: - vals, err = n.EvalModuleCallArgument(ctx, false) + case walkValidate: + vals, err = n.EvalModuleCallArgument(ctx, true) if err != nil { return err } - case walkValidate: - vals, err = n.EvalModuleCallArgument(ctx, true) + default: + vals, err = n.EvalModuleCallArgument(ctx, false) if err != nil { return err } diff --git a/terraform/node_output.go b/terraform/node_output.go index ec4f48276..439c2a9cf 100644 --- a/terraform/node_output.go +++ b/terraform/node_output.go @@ -199,61 +199,55 @@ func (n *NodeApplyableOutput) References() []*addrs.Reference { // GraphNodeExecutable func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error { - switch op { - // Everything except walkImport - case walkEval, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy: - // This has to run before we have a state lock, since evaluation also - // reads the state - val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil) - // We'll handle errors below, after we have loaded the module. + // This has to run before we have a state lock, since evaluation also + // reads the state + val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil) + // We'll handle errors below, after we have loaded the module. - // Outputs don't have a separate mode for validation, so validate - // depends_on expressions here too - diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) + // Outputs don't have a separate mode for validation, so validate + // depends_on expressions here too + diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn)) - // Ensure that non-sensitive outputs don't include sensitive values - _, marks := val.UnmarkDeep() - _, hasSensitive := marks["sensitive"] - if !n.Config.Sensitive && hasSensitive { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Output refers to sensitive values", - Detail: "Expressions used in outputs can only refer to sensitive values if the sensitive attribute is true.", - Subject: n.Config.DeclRange.Ptr(), - }) - } + // Ensure that non-sensitive outputs don't include sensitive values + _, marks := val.UnmarkDeep() + _, hasSensitive := marks["sensitive"] + if !n.Config.Sensitive && hasSensitive { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Output refers to sensitive values", + Detail: "Expressions used in outputs can only refer to sensitive values if the sensitive attribute is true.", + Subject: n.Config.DeclRange.Ptr(), + }) + } - state := ctx.State() - if state == nil { - return nil - } - - changes := ctx.Changes() // may be nil, if we're not working on a changeset - - // handling the interpolation error - if diags.HasErrors() { - if flagWarnOutputErrors { - log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr, diags.Err()) - // if we're continuing, make sure the output is included, and - // marked as unknown. If the evaluator was able to find a type - // for the value in spite of the error then we'll use it. - n.setValue(state, changes, cty.UnknownVal(val.Type())) - return EvalEarlyExitError{} - } - return diags.Err() - } - n.setValue(state, changes, val) - - // If we were able to evaluate a new value, we can update that in the - // refreshed state as well. - if state = ctx.RefreshState(); state != nil && val.IsWhollyKnown() { - n.setValue(state, changes, val) - } - - return nil - default: + state := ctx.State() + if state == nil { return nil } + + changes := ctx.Changes() // may be nil, if we're not working on a changeset + + // handling the interpolation error + if diags.HasErrors() { + if flagWarnOutputErrors { + log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr, diags.Err()) + // if we're continuing, make sure the output is included, and + // marked as unknown. If the evaluator was able to find a type + // for the value in spite of the error then we'll use it. + n.setValue(state, changes, cty.UnknownVal(val.Type())) + return EvalEarlyExitError{} + } + return diags.Err() + } + n.setValue(state, changes, val) + + // If we were able to evaluate a new value, we can update that in the + // refreshed state as well. + if state = ctx.RefreshState(); state != nil && val.IsWhollyKnown() { + n.setValue(state, changes, val) + } + + return nil } // dag.GraphNodeDotter impl. diff --git a/terraform/node_resource_destroy.go b/terraform/node_resource_destroy.go index c54bc0cec..872df3654 100644 --- a/terraform/node_resource_destroy.go +++ b/terraform/node_resource_destroy.go @@ -136,133 +136,130 @@ func (n *NodeDestroyResourceInstance) Execute(ctx EvalContext, op walkOperation) var state *states.ResourceInstanceObject var provisionerErr error - switch op { - case walkApply, walkDestroy: - provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) + provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider) + if err != nil { + return err + } + + changeApply, err = n.readDiff(ctx, providerSchema) + if err != nil { + return err + } + + evalReduceDiff := &EvalReduceDiff{ + Addr: addr.Resource, + InChange: &changeApply, + Destroy: true, + OutChange: &changeApply, + } + _, err = evalReduceDiff.Eval(ctx) + if err != nil { + return err + } + + // EvalReduceDiff may have simplified our planned change + // into a NoOp if it does not require destroying. + if changeApply == nil || changeApply.Action == plans.NoOp { + return EvalEarlyExitError{} + } + + state, err = n.ReadResourceInstanceState(ctx, addr) + if err != nil { + return err + } + + // Exit early if the state object is null after reading the state + if state == nil || state.Value.IsNull() { + return EvalEarlyExitError{} + } + + evalApplyPre := &EvalApplyPre{ + Addr: addr.Resource, + State: &state, + Change: &changeApply, + } + _, err = evalApplyPre.Eval(ctx) + if err != nil { + return err + } + + // Run destroy provisioners if not tainted + if state != nil && state.Status != states.ObjectTainted { + evalApplyProvisioners := &EvalApplyProvisioners{ + Addr: addr.Resource, + State: &state, + ResourceConfig: n.Config, + Error: &provisionerErr, + When: configs.ProvisionerWhenDestroy, + } + _, err := evalApplyProvisioners.Eval(ctx) if err != nil { return err } - - changeApply, err = n.readDiff(ctx, providerSchema) - if err != nil { - return err - } - - evalReduceDiff := &EvalReduceDiff{ - Addr: addr.Resource, - InChange: &changeApply, - Destroy: true, - OutChange: &changeApply, - } - _, err = evalReduceDiff.Eval(ctx) - if err != nil { - return err - } - - // EvalReduceDiff may have simplified our planned change - // into a NoOp if it does not require destroying. - if changeApply == nil || changeApply.Action == plans.NoOp { - return EvalEarlyExitError{} - } - - state, err = n.ReadResourceInstanceState(ctx, addr) - if err != nil { - return err - } - - // Exit early if the state object is null after reading the state - if state == nil || state.Value.IsNull() { - return EvalEarlyExitError{} - } - - evalApplyPre := &EvalApplyPre{ - Addr: addr.Resource, - State: &state, - Change: &changeApply, - } - _, err = evalApplyPre.Eval(ctx) - if err != nil { - return err - } - - // Run destroy provisioners if not tainted - if state != nil && state.Status != states.ObjectTainted { - evalApplyProvisioners := &EvalApplyProvisioners{ - Addr: addr.Resource, - State: &state, - ResourceConfig: n.Config, - Error: &provisionerErr, - When: configs.ProvisionerWhenDestroy, + if provisionerErr != nil { + // If we have a provisioning error, then we just call + // the post-apply hook now. + evalApplyPost := &EvalApplyPost{ + Addr: addr.Resource, + State: &state, + Error: &provisionerErr, } - _, err := evalApplyProvisioners.Eval(ctx) + _, err = evalApplyPost.Eval(ctx) if err != nil { return err } - if provisionerErr != nil { - // If we have a provisioning error, then we just call - // the post-apply hook now. - evalApplyPost := &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &provisionerErr, - } - _, err = evalApplyPost.Eval(ctx) - if err != nil { - return err - } - } } + } - // Managed resources need to be destroyed, while data sources - // are only removed from state. - if addr.Resource.Resource.Mode == addrs.ManagedResourceMode { - evalApply := &EvalApply{ - Addr: addr.Resource, - Config: nil, // No configuration because we are destroying - State: &state, - Change: &changeApply, - Provider: &provider, - ProviderAddr: n.ResolvedProvider, - ProviderMetas: n.ProviderMetas, - ProviderSchema: &providerSchema, - Output: &state, - Error: &provisionerErr, - } - _, err = evalApply.Eval(ctx) - if err != nil { - return err - } - - evalWriteState := &EvalWriteState{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - ProviderSchema: &providerSchema, - State: &state, - } - _, err = evalWriteState.Eval(ctx) - if err != nil { - return err - } - } else { - log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr) - state := ctx.State() - state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + // Managed resources need to be destroyed, while data sources + // are only removed from state. + if addr.Resource.Resource.Mode == addrs.ManagedResourceMode { + evalApply := &EvalApply{ + Addr: addr.Resource, + Config: nil, // No configuration because we are destroying + State: &state, + Change: &changeApply, + Provider: &provider, + ProviderAddr: n.ResolvedProvider, + ProviderMetas: n.ProviderMetas, + ProviderSchema: &providerSchema, + Output: &state, + Error: &provisionerErr, } - - evalApplyPost := &EvalApplyPost{ - Addr: addr.Resource, - State: &state, - Error: &provisionerErr, - } - _, err = evalApplyPost.Eval(ctx) + _, err = evalApply.Eval(ctx) if err != nil { return err } - err = UpdateStateHook(ctx) + evalWriteState := &EvalWriteState{ + Addr: addr.Resource, + ProviderAddr: n.ResolvedProvider, + ProviderSchema: &providerSchema, + State: &state, + } + _, err = evalWriteState.Eval(ctx) if err != nil { return err } + } else { + log.Printf("[TRACE] NodeDestroyResourceInstance: removing state object for %s", n.Addr) + state := ctx.State() + state.SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider) + } + + evalApplyPost := &EvalApplyPost{ + Addr: addr.Resource, + State: &state, + Error: &provisionerErr, + } + _, err = evalApplyPost.Eval(ctx) + if err != nil { + return err + } + + err = UpdateStateHook(ctx) + if err != nil { + return err } return nil diff --git a/terraform/node_resource_destroy_deposed.go b/terraform/node_resource_destroy_deposed.go index 6d5af049b..a77cdf476 100644 --- a/terraform/node_resource_destroy_deposed.go +++ b/terraform/node_resource_destroy_deposed.go @@ -76,44 +76,41 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk var change *plans.ResourceInstanceChange var state *states.ResourceInstanceObject - switch op { - case walkPlan, walkPlanDestroy: - - readStateDeposed := &EvalReadStateDeposed{ - Addr: addr.Resource, - Output: &state, - Key: n.DeposedKey, - Provider: &provider, - ProviderSchema: &providerSchema, - } - _, err = readStateDeposed.Eval(ctx) - if err != nil { - return err - } - - diffDestroy := &EvalDiffDestroy{ - Addr: addr.Resource, - ProviderAddr: n.ResolvedProvider, - DeposedKey: n.DeposedKey, - State: &state, - Output: &change, - } - _, err = diffDestroy.Eval(ctx) - if err != nil { - return err - } - - writeDiff := &EvalWriteDiff{ - Addr: addr.Resource, - DeposedKey: n.DeposedKey, - ProviderSchema: &providerSchema, - Change: &change, - } - _, err = writeDiff.Eval(ctx) - if err != nil { - return err - } + readStateDeposed := &EvalReadStateDeposed{ + Addr: addr.Resource, + Output: &state, + Key: n.DeposedKey, + Provider: &provider, + ProviderSchema: &providerSchema, } + _, err = readStateDeposed.Eval(ctx) + if err != nil { + return err + } + + diffDestroy := &EvalDiffDestroy{ + Addr: addr.Resource, + ProviderAddr: n.ResolvedProvider, + DeposedKey: n.DeposedKey, + State: &state, + Output: &change, + } + _, err = diffDestroy.Eval(ctx) + if err != nil { + return err + } + + writeDiff := &EvalWriteDiff{ + Addr: addr.Resource, + DeposedKey: n.DeposedKey, + ProviderSchema: &providerSchema, + Change: &change, + } + _, err = writeDiff.Eval(ctx) + if err != nil { + return err + } + return nil } diff --git a/terraform/transform_config.go b/terraform/transform_config.go index 95606dd71..35f448490 100644 --- a/terraform/transform_config.go +++ b/terraform/transform_config.go @@ -25,9 +25,6 @@ type ConfigTransformer struct { // Module is the module to add resources from. Config *configs.Config - // Unique will only add resources that aren't already present in the graph. - Unique bool - // Mode will only add resources that match the given mode ModeFilter bool Mode addrs.ResourceMode diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index b813226f5..0b7d0e566 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -301,7 +301,7 @@ func (m ReferenceMap) References(v dag.Vertex) []dag.Vertex { case addrs.ModuleCallInstance: subject = ri.Call default: - log.Printf("[WARN] ReferenceTransformer: reference not found: %q", subject) + log.Printf("[INFO] ReferenceTransformer: reference not found: %q", subject) continue } key = m.referenceMapKey(v, subject)