mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
Most of our transformers are pure compute and so don't really have a strong need to generate trace spans under our current focus of only exposing user-facing concepts and external requests in our traces, but unfortunately some of them indirectly depend on provider schema, which in turn means that they can potentially be unlucky enough to be the trigger for making all of the provider requests needed to fill the schema cache and therefore would end up with provider request spans being reported beneath them. As usual with these interface updates, this initial change focuses only on changing the interface and updating its direct callers and implementers to match, without any further refactoring or attempts to plumb contexts to or from other functions that don't have them yet. That means there are a few new context.TODO() calls here that we'll tidy up in a later commit that hopefully won't involve all of the noise that is caused by changing an interface API. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
149 lines
4.5 KiB
Go
149 lines
4.5 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package tofu
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/configs"
|
|
"github.com/opentofu/opentofu/internal/dag"
|
|
)
|
|
|
|
type checkTransformer struct {
|
|
// Config for the entire module.
|
|
Config *configs.Config
|
|
|
|
// Operation is the current operation this node will be part of.
|
|
Operation walkOperation
|
|
}
|
|
|
|
var _ GraphTransformer = (*checkTransformer)(nil)
|
|
|
|
func (t *checkTransformer) Transform(_ context.Context, graph *Graph) error {
|
|
return t.transform(graph, t.Config, graph.Vertices())
|
|
}
|
|
|
|
func (t *checkTransformer) transform(g *Graph, cfg *configs.Config, allNodes []dag.Vertex) error {
|
|
|
|
if t.Operation == walkDestroy || t.Operation == walkPlanDestroy {
|
|
// Don't include anything about checks during destroy operations.
|
|
//
|
|
// For other plan and normal apply operations we do everything, for
|
|
// destroy operations we do nothing. For any other operations we still
|
|
// include the check nodes, but we don't actually execute the checks
|
|
// instead we still validate their references and make sure their
|
|
// conditions make sense etc.
|
|
return nil
|
|
}
|
|
|
|
moduleAddr := cfg.Path
|
|
|
|
for _, check := range cfg.Module.Checks {
|
|
configAddr := check.Addr().InModule(moduleAddr)
|
|
|
|
// We want to create a node for each check block. This node will execute
|
|
// after anything it references, and will update the checks object
|
|
// embedded in the plan and/or state.
|
|
|
|
log.Printf("[TRACE] checkTransformer: Nodes and edges for %s", configAddr)
|
|
expand := &nodeExpandCheck{
|
|
addr: configAddr,
|
|
config: check,
|
|
makeInstance: func(addr addrs.AbsCheck, cfg *configs.Check) dag.Vertex {
|
|
return &nodeCheckAssert{
|
|
addr: addr,
|
|
config: cfg,
|
|
executeChecks: t.ExecuteChecks(),
|
|
}
|
|
},
|
|
}
|
|
g.Add(expand)
|
|
|
|
// We also need to report the checks we are going to execute before we
|
|
// try and execute them.
|
|
if t.ReportChecks() {
|
|
report := &nodeReportCheck{
|
|
addr: configAddr,
|
|
}
|
|
g.Add(report)
|
|
|
|
// Make sure we report our checks before we start executing the
|
|
// actual checks.
|
|
g.Connect(dag.BasicEdge(expand, report))
|
|
|
|
if check.DataResource != nil {
|
|
// If we have a nested data source, we need to make sure we
|
|
// also report the check before the data source executes.
|
|
//
|
|
// We loop through all the nodes in the graph to find the one
|
|
// that contains our data source and connect it.
|
|
for _, other := range allNodes {
|
|
if resource, isResource := other.(GraphNodeConfigResource); isResource {
|
|
resourceAddr := resource.ResourceAddr()
|
|
if !resourceAddr.Module.Equal(moduleAddr) {
|
|
// This resource isn't in the same module as our check
|
|
// so skip it.
|
|
continue
|
|
}
|
|
|
|
resourceCfg := cfg.Module.ResourceByAddr(resourceAddr.Resource)
|
|
if resourceCfg != nil && resourceCfg.Container != nil && resourceCfg.Container.Accessible(check.Addr()) {
|
|
// Make sure we report our checks before we execute any
|
|
// embedded data resource.
|
|
g.Connect(dag.BasicEdge(other, report))
|
|
|
|
// There's at most one embedded data source, and
|
|
// we've found it so stop looking.
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, child := range cfg.Children {
|
|
if err := t.transform(g, child, allNodes); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReportChecks returns true if this operation should report any check blocks
|
|
// that it is about to execute.
|
|
//
|
|
// This is true for planning operations, as apply operations recreate the
|
|
// expected checks from the plan.
|
|
//
|
|
// We'll also report the checks during an import operation. We still execute
|
|
// our check blocks during an import operation so they need to be reported
|
|
// first.
|
|
func (t *checkTransformer) ReportChecks() bool {
|
|
return t.Operation == walkPlan || t.Operation == walkImport
|
|
}
|
|
|
|
// ExecuteChecks returns true if this operation should actually execute any
|
|
// check blocks in the config.
|
|
//
|
|
// If this returns false we will still create and execute check nodes in the
|
|
// graph, but they will only validate things like references and syntax.
|
|
func (t *checkTransformer) ExecuteChecks() bool {
|
|
switch t.Operation {
|
|
case walkPlan, walkApply, walkImport:
|
|
// We only actually execute the checks for plan and apply operations.
|
|
return true
|
|
default:
|
|
// For everything else, we still want to validate the checks make sense
|
|
// logically and syntactically, but we won't actually resolve the check
|
|
// conditions.
|
|
return false
|
|
}
|
|
}
|