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>
134 lines
3.9 KiB
Go
134 lines
3.9 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"
|
|
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/internal/configs"
|
|
"github.com/opentofu/opentofu/internal/dag"
|
|
)
|
|
|
|
var _ GraphTransformer = (*checkStartTransformer)(nil)
|
|
|
|
// checkStartTransformer checks if the configuration has any data blocks nested
|
|
// within check blocks, and if it does then it introduces a nodeCheckStart
|
|
// vertex that ensures all resources have been applied before it starts loading
|
|
// the nested data sources.
|
|
type checkStartTransformer struct {
|
|
// Config for the entire module.
|
|
Config *configs.Config
|
|
|
|
// Operation is the current operation this node will be part of.
|
|
Operation walkOperation
|
|
}
|
|
|
|
func (s *checkStartTransformer) Transform(_ context.Context, graph *Graph) error {
|
|
if s.Operation != walkApply && s.Operation != walkPlan {
|
|
// We only actually execute the checks during plan apply operations
|
|
// so if we are doing something else we can just skip this and
|
|
// leave the graph alone.
|
|
return nil
|
|
}
|
|
|
|
var resources []dag.Vertex
|
|
var nested []dag.Vertex
|
|
|
|
// We're going to step through all the vertices and pull out the relevant
|
|
// resources and data sources.
|
|
for _, vertex := range graph.Vertices() {
|
|
if node, isResource := vertex.(GraphNodeCreator); isResource {
|
|
addr := node.CreateAddr()
|
|
|
|
if addr.Resource.Resource.Mode == addrs.ManagedResourceMode {
|
|
// This is a resource, so we want to make sure it executes
|
|
// before any nested data sources.
|
|
|
|
// We can reduce the number of additional edges we write into
|
|
// the graph by only including "leaf" resources, that is
|
|
// resources that aren't referenced by other resources. If a
|
|
// resource is referenced by another resource then we know that
|
|
// it will execute before that resource so we only need to worry
|
|
// about the referencing resource.
|
|
|
|
leafResource := true
|
|
for _, other := range graph.UpEdges(vertex) {
|
|
if otherResource, isResource := other.(GraphNodeCreator); isResource {
|
|
otherAddr := otherResource.CreateAddr()
|
|
if otherAddr.Resource.Resource.Mode == addrs.ManagedResourceMode {
|
|
// Then this resource is being referenced so skip
|
|
// it.
|
|
leafResource = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if leafResource {
|
|
resources = append(resources, vertex)
|
|
}
|
|
|
|
// We've handled the resource so move to the next vertex.
|
|
continue
|
|
}
|
|
|
|
// Now, we know we are processing a data block.
|
|
|
|
config := s.Config
|
|
if !addr.Module.IsRoot() {
|
|
config = s.Config.Descendent(addr.Module.Module())
|
|
}
|
|
if config == nil {
|
|
// might have been deleted, so it won't be subject to any checks
|
|
// anyway.
|
|
continue
|
|
}
|
|
|
|
resource := config.Module.ResourceByAddr(addr.Resource.Resource)
|
|
if resource == nil {
|
|
// might have been deleted, so it won't be subject to any checks
|
|
// anyway.
|
|
continue
|
|
}
|
|
|
|
if _, ok := resource.Container.(*configs.Check); ok {
|
|
// Then this is a data source within a check block, so let's
|
|
// make a note of it.
|
|
nested = append(nested, vertex)
|
|
}
|
|
|
|
// Otherwise, it's just a normal data source. From a check block we
|
|
// don't really care when OpenTofu is loading non-nested data
|
|
// sources so we'll just forget about it and move on.
|
|
}
|
|
}
|
|
|
|
if len(nested) > 0 {
|
|
|
|
// We don't need to do any of this if we don't have any nested data
|
|
// sources, so we check that first.
|
|
//
|
|
// Otherwise we introduce a vertex that can act as a pauser between
|
|
// our nested data sources and leaf resources.
|
|
|
|
check := &nodeCheckStart{}
|
|
graph.Add(check)
|
|
|
|
// Finally, connect everything up so it all executes in order.
|
|
|
|
for _, vertex := range nested {
|
|
graph.Connect(dag.BasicEdge(vertex, check))
|
|
}
|
|
|
|
for _, vertex := range resources {
|
|
graph.Connect(dag.BasicEdge(check, vertex))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|