mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-21 10:47:34 -05:00
During early experiments the logic for "compiling" a configs.Module into a bunch of configgraph node objects was just a bunch of unexported functions directly in package eval, because it wasn't clear yet what abstraction would make most sense between those two. Now that this has solidified a little more this commit is a first attempt at drawing a line between packages implementing "compilation" for specific editions of the language (for which there is currently only one, which I've named "tofu2024" as a placeholder because Terraform's only existing language was similarly called "TF2021") and the top-level evaluation package that tries to be as edition-agnostic as possible, although it is currently still the one responsible for deciding to call into tofu2024. There doesn't seem much point in abstracting away the decision about what edition to use until we actually have a plan to introduce a new edition and therefore have some idea of what the design constraints for that might turn out to be. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
74 lines
3.1 KiB
Go
74 lines
3.1 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 eval
|
|
|
|
import (
|
|
"context"
|
|
"iter"
|
|
|
|
"github.com/apparentlymart/go-workgraph/workgraph"
|
|
|
|
"github.com/opentofu/opentofu/internal/lang/eval/internal/evalglue"
|
|
"github.com/opentofu/opentofu/internal/lang/grapheval"
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
)
|
|
|
|
// checkAll walks the configuration tree starting at the given root module
|
|
// instance, collecting diagnostics that describe problems with the
|
|
// configuration (but NOT problems outside of the configuration, such as
|
|
// apply-time operation failures).
|
|
//
|
|
// This is really just a wrapper around calling
|
|
// [configgraph.ModuleInstance.CheckAll], but it's important to use this
|
|
// because it arranges for tracking workgraph request IDs so we can return
|
|
// helpful error messages when expression evaluation encounters a
|
|
// self-dependency problem.
|
|
func checkAll(ctx context.Context, rootModuleInstance evalglue.CompiledModuleInstance) tfdiags.Diagnostics {
|
|
// If the grapheval package detects a self-dependency problem during
|
|
// evaluation then it'll use this tracker to find human-friendly names
|
|
// for all of the requests involved in the error.
|
|
ctx = grapheval.ContextWithRequestTracker(ctx, workgraphRequestTracker{rootModuleInstance})
|
|
return rootModuleInstance.CheckAll(ctx)
|
|
}
|
|
|
|
// workgraphRequestTracker is an awkward piece of glue that helps the
|
|
// code in [grapheval] to find user-friendly names for requests in progress
|
|
// when it needs to report errors.
|
|
//
|
|
// The weird indirection here is a compromise to keep this record-tracking
|
|
// outside of the main "happy path" code, both because maintainers shouldn't
|
|
// need to think about it most of the time and because we then don't need
|
|
// to do this request-tracking work unless a grapheval-related error actually
|
|
// occurs, since such errors ought to be rare.
|
|
type workgraphRequestTracker struct {
|
|
rootModuleInstance evalglue.CompiledModuleInstance
|
|
}
|
|
|
|
// ActiveRequests implements grapheval.RequestTracker.
|
|
func (w workgraphRequestTracker) ActiveRequests() iter.Seq2[workgraph.RequestID, grapheval.RequestInfo] {
|
|
return func(yield func(workgraph.RequestID, grapheval.RequestInfo) bool) {
|
|
// Since we only call into AnnounceAllGraphevalRequests on an error
|
|
// path anyway, we'll make the code in there a little simpler by
|
|
// always announcing every request and just ignore any announcements
|
|
// that come after the consumer of our sequence has asked us to stop.
|
|
// (In practice the caller in grapheval always consumes the full
|
|
// sequence anyway, so this is just for completeness to make sure
|
|
// we always follow the [iter.Seq2] conventions.)
|
|
callerDone := false
|
|
w.rootModuleInstance.AnnounceAllGraphevalRequests(func(reqID workgraph.RequestID, info grapheval.RequestInfo) {
|
|
if callerDone {
|
|
return // caller doesn't want to hear from us anymore
|
|
}
|
|
if reqID == workgraph.NoRequest {
|
|
return // ignore announcements of requests that haven't actually started
|
|
}
|
|
if !yield(reqID, info) {
|
|
callerDone = true // all future announcements will be silently discarded
|
|
}
|
|
})
|
|
}
|
|
}
|