Merge pull request #26495 from hashicorp/jbardin/evaluate-walks

Audit some graph building and GraphNodeExecutable
This commit is contained in:
James Bardin 2020-10-07 09:20:05 -04:00 committed by GitHub
commit 496cf629cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 232 additions and 282 deletions

View File

@ -38,7 +38,7 @@ func TestContextEval(t *testing.T) {
},
{
`module.child.result`,
cty.UnknownVal(cty.Number),
cty.NumberIntVal(6),
false,
},
}

View File

@ -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,

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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{},

View File

@ -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
}

View File

@ -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.

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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)