-
Notifications
You must be signed in to change notification settings - Fork 9.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
core: Plumbing for graph nodes that belong to partially-expanded module prefixes #34571
Conversation
We previously had the StaticValidateReferences method implemented on the evaluationStateData type directly, but really the only information it needs directly from that object is the static module path, with everything else just coming from the wrapped Evaluator. That means that we can instead implement this as a method of Evaluator that is thinly wrapped by the prior evaluationStateData method. The guts of that method are functions that don't really depend on either of the two objects as a whole, so thus further turns them into just plain unexported functions that take the data they need as arguments. This commit doesn't achieve anything other than some light refactoring, but it will become more important in a subsequent commit that will add an additional implementation of lang.Data for evaluating references in expressions beneath an incompletely-expanded module. Both of those implementations can safely share the same implementation of StaticValidateReference though, since that method doesn't consider any dynamic expansion by definition anyway.
When we're evaluating expressions inside a module that's beneath a partially-expanded prefix, we need to use placeholder values that describe only what we know to be true for all possible instances within that prefix, which requires quite a different reference evaluation strategy than we'd use in a fully-expanded module instance. evaluationPlaceholderData will therefore substitute for evaluationStateData in such cases. It is associated with an unbounded set of possible module instances sharing a known module instance prefix, and uses placeholder data generated by special graph nodes representing unexpanded objects, instead of finalized data coming from the main state or plan. As of this commit, the new type is not used at all. In subsequent commits we'll teach terraform.EvalContextBuiltin to support working under unexpanded module prefixes, and then this new lang.Data type will be part of the implementation of its expression-evaluation methods in that case.
Previously an EvalContextBuiltin could be associated either with no module at all or with a fully-qualified module instance, depending on whether each graph node implements the interface required to announce association with a particular module instance. For supporting partial-expanded module prefixes we now need a third mode where the context is associated with an addrs.PartialExpandedModule address, which conceptually represents an unbounded set of possible module instances that share a common known address prefix. The new type evalContextScope serves as a sum type which represents these three different situations. A nil value of that interface type represents no scope at all, whereas the two implementations of that type represent the two different possible scope types. Both scope types can produce an addrs.Module that they represent, allowing some degree of shared code between the two cases as long as no full instance addresses are required. The most important part of this change is that method EvaluationScope now uses a different implementation of lang.Data when the context scope is a partial-expanded module address. That implementation only returns placeholders that approximate what we expect all instances of the affected modules to have in common, in the hope of still catching some downstream errors even though full evaluation is being deferred to a future run. We currently have no means for a graph node to announce itself as belonging to a partially-expanded module address, and so in practice this new mode is not yet reachable. Future commits will teach the graph walker to be able to recognize and handle graph nodes that need this new treatment, making their DynamicExpand/Execute methods use this new scope type.
Nothing calls this yet, because we don't yet have an interface for a graph node to declare that it belongs to a partially-evaluated module prefix address. That will follow in later commits.
The new interface GraphNodePartialExpandedModule is a mutually-exclusive alternative to GraphNodeModuleInstance for graph nodes that represent the unbounded set of as-yet-unknown instances sharing a common known module prefix. For nodes that implement this interface, their Execute and DynamicExpand methods will receive an EvalContext that is configured to perform expression evaluation using safe placeholder values instead of using the real state and plan, since (once implemented in future commits) the graph nodes of this sort will be used for situations where we're deferring part of the plan until we have more information to use while planning.
75e1fde
to
9d4f44d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense to me!
Reminder for the merging maintainer: if this is a user-visible change, please update the changelog on the appropriate release branch. |
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions. |
This is a further iteration towards #30937, and follows on from yesterday's #34561.
This particular PR deals with plumbing in the evaluation nuts-and-bolts for graph nodes that are representing unbounded sets of as-yet-unknown instances of objects that are declared beneath a module call that has an insufficiently-known
count
orfor_each
argument.This has a few different parts:
A new implementation of
lang.Data
that's designed to return placeholder values that should represent what all instances of a given object have in common, while using unknown values for the parts where they differ.In this initial round it's quite meagre and returns
cty.DynamicVal
in some situations where it could technically be more precise than that, but we can improve the precision later to give earlier feedback about problems that would definitely cause a failure on a subsequent plan.EvalContext
(or rather the concreteEvalContextBuiltin
implementation of it) now has a third mode for evaluating in a partially-unknown module prefix, in addition to its existing modes for not being in a module at all or being in a fully-expanded module instance.I modeled this new tristate situation by replacing the previous
PathValue
andpathSet
fields with a single fieldscope
of an interface type, where that interface type is acting as a closed sum type covering the three different variants. The small amount of logic inEvalContextBuiltin
that varies based on the module path then uses type assertions and type switches to distinguish between the three cases.The "context graph walker" now knows of a new extension interface for graph nodes which announces that a node belongs to a partial-expanded module prefix. For graph nodes that implement that interface, their
Execute
andEvalContext
methods get anEvalContext
in the partially-unknown module prefix mode.I intentionally chose the same method name for the new interface as for the existing interface used for
ModuleInstance
affinity, but with a different signature, to statically represent that these two interfaces are mutually-exclusive: a graph node cannot be evaluated in two different contexts at once.For good measure I also retrofitted the three stubby node types added previously to represent partial-expanded named values with implementations of the new interface, so once they have
Execute
implemented they'll get a suitably-configuredEvalContext
. However, the actualExecute
implementations are not included here, and will follow in future commits.Reviewers might prefer to take this on a commit-by-commit basis; the series of commits follows roughly the sequence of changes I described above.
Therefore this PR is just another nothingburger as far as functionality is concerned -- no externally-visible behavior should be any different than it was before -- but this is an internal milestone where we now have all of the shared plumbing implemented, so future PRs can implement the language-feature-specific evaluation logic in terms of this. Again, there are no new integration tests here because there isn't yet any real new behavior to test, but those too will follow along with the language-feature-specific implementations in later PRs.