Skip to content
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

[WIP] Ephemeral Values prototype #35077

Closed
wants to merge 17 commits into from
Closed

Commits on Nov 29, 2023

  1. providers: Allow providers to "defer" certain requests

    For any request that can occur during the planning phase there is a chance
    that either a resource configuration or its associated provider
    configuration will contain unknown values that are placeholders for
    results of operations that haven't yet completed.
    
    Ideally a provider would be able to just do its best to predict the
    outcome in spite of the partial information, but in practice that isn't
    always possible. In those more complex situations it's better to let the
    provider explicitly decline to complete the operation and have Terraform
    Core defer it for a future run when there's hopefully more information
    available due to having applied other changes upstream.
    
    This commit does not yet introduce the idea of "deferred changes" into
    Terraform Core, so as a temporary step Terraform Core will just return
    an error if a provider tries to defer anything. In future commits we'll
    teach Terraform Core how to handle this more gracefully by saving partial
    results into the plan as "deferred changes" and then continuing on to
    downstream resources to try to gather as much information as possible to
    help the user understand the likely effects of those deferred actions.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    3ec4e9b View commit details
    Browse the repository at this point in the history
  2. addrs: Deferrable address types

    This represents the two address types that could potentially have deferred
    actions associated with them during a Terraform plan operation, because
    deferring can happen either before or after instance expansion.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    b5296a4 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    0c595bf View commit details
    Browse the repository at this point in the history
  4. core: Register unknown count and for_each with instance expander

    Previously we just immediately bailed out with an error if either count
    or for_each were not sufficiently known to determine their full set of
    instance keys.
    
    The Expander abstraction can now talk about module calls and resources
    having unknown expansion, so Terraform Core should tolerate that situation
    and just let the expander know that the expansion is unknown, and then
    we'll deal with that situation downstream.
    
    For now "downstream" actually means directly after these functions return,
    because the rest of Terraform Core isn't yet ready to deal with objects
    that don't know their full expansions. We'll just return errors similar
    to (but slightly lower quality than) the ones we used to return during
    evaluation, as a temporary placeholder to keep things working until we
    get downstream more ready to deal with this.
    
    While working on this I also noticed that we were redundantly
    re-evaluating the for_each expressions for each resource instance just to
    prepare the repetition data, which is unnecessary because the Expander
    abstraction already keeps track of that to ensure that all of the graph
    nodes have a consistent view of the expansions. We'll now just ask the
    expander directly what our RepetitionData should be, since that's part
    of the expander's responsibility.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    c7d76f0 View commit details
    Browse the repository at this point in the history
  5. addrs: Getting the parent of a PartialExpandedModule

    Traversing upward from a PartialExpandedModule is trickier than traversing
    down because we need to deal with what happens if the traversal crosses
    over the boundary from partial-expanded into fully-expanded.
    
    To deal with that we end up having two different methods to handle the
    two situations, and a third method to indicate which one to call.
    Thankfully the need to ask for the parent of a partial-expanded module is
    relatively rare -- mainly just for input variables whose definitions need
    to eval in the parent module's scope -- so this awkward API shouldn't be
    needed in two many places.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    f7f210e View commit details
    Browse the repository at this point in the history
  6. terraform: Initial implementation of partial-expanded input variables

    This is mainly just a proof-of-concept of what it might look like to
    generate graph nodes representing placeholders for objects in
    not-fully-expanded modules. These new codepaths are not really accessible
    yet because it's still invalid to have a module whose expansion is
    unknown; we'll continue down this path further in later commits once
    there's actually somewhere to save the partially-evaluated placeholder
    values.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    40d525f View commit details
    Browse the repository at this point in the history
  7. core: Graph walk is aware of partial-expanded module paths

    Our evaluation strategy for module-namespaced objects unfortunately
    depends quite strongly on having the right EvalContext in scope for each
    graph node, referring to the appropriate namespace in which to evaluate
    expressions.
    
    Although I was pretty reluctant to integrate the idea of partial-expanded
    module paths at quite this low a level, it does seem like the most
    pragmatic answer since it works with rather than against the existing
    evaluation strategies.
    
    As of this commit this isn't really doing anything because it isn't
    possible to reach any graph node that has a partial-expanded path and the
    EvalContext itself doesn't actually properly support evaluation in a
    partial-expanded path anyway; we'll fix up the rest of this in later
    commits before making these codepaths reachable.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    b000ef5 View commit details
    Browse the repository at this point in the history
  8. core: use namedvals.State to track input variable evaluation

    This replaces the direct manipulation of a map shared between three
    different components, encapsulating that manipulation now inside a single
    wrapping API that itself ensures safe concurrent access.
    
    In future commits we'll do the same for local values and output values,
    but for now those part of namedvals.State remain unused.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    cd4a221 View commit details
    Browse the repository at this point in the history
  9. core: Remove variable-specific methods from EvalContext

    Now that we have the separate namedvals.State type to encapsulate all of
    the named-value tracking we can simplify the EvalContext API to just
    return that object directly.
    
    This removes the slightly odd evolved API for setting and retrieving
    input variable values, instead now just calling directly into the relevant
    namedvals.State methods. It also slightly simplifies some of our test
    code because there's no longer any need to mock accesses to what is just
    a temporary in-memory data store anyway.
    
    Finally, this now gives nodePartialExpandedModuleVariable somewhere to
    save its placeholder values, though there's not yet anything to read them.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    52bafe3 View commit details
    Browse the repository at this point in the history
  10. core: Start of integrating "partial eval" mode into the evaluator

    This is a new mode for the evaluator where instead of returning
    information about exact objects it'll return placeholder values that
    represent potentially many different hypothetical objects all declared
    from the same static configuration object, in situations where we don't
    yet have enough information to expand all of the modules and their
    contents.
    
    So far only the GetInputVariable function actually knows how to deal with
    this, so this is far from sufficient but is a reasonable starting point
    just to establish that it's possible to get Terraform into this evaluation
    mode when working with graph nodes that represent such placeholder objects.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    60fdd21 View commit details
    Browse the repository at this point in the history
  11. states: Local values no longer live in state

    Back when we added local values (a long time ago now!) we put their
    results in state mainly just because it was the only suitable shared data
    structure to keep them in. They are a bit ideosyncratic there because we
    intentionally discard them when serializing state to a snapshot, and
    that's just fine because they never need to be retained between runs
    anyway.
    
    We now have namedvals.State for all of our named value result storage
    needs, so we can remove the local-value-related fields of states.Module
    and use the relevant map inside the local value state instead.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    36307c2 View commit details
    Browse the repository at this point in the history
  12. core: Evaluate placeholders for local values in unexpanded modules

    For any local value declared beneath a module call whose expansion isn't
    known yet, we'll calculate a single value to serve as a placeholder for
    all possible valid instances of that local value, using unknown values
    in any situation where a value might differ between instances.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    73a82df View commit details
    Browse the repository at this point in the history
  13. states: Only track root module output values

    For a very long time we've had an annoying discrepancy between the
    in-memory state model and our state snapshot format where the in-memory
    format stores output values for all modules whereas the snapshot format
    only tracks the root module output values because those are all we
    actually need to preserve between runs.
    
    That design wart was a result of us using the state both as an internal
    and an external artifact, due to having nowhere else to store the
    transient values of non-root module output values while Terraform Core
    does its work.
    
    We now have namedvals.State to internally track all of the throwaway
    results from named values that don't need to persist between runs, so now
    we'll use that for our internal work instead and reserve the states.State
    model only for the data that we will preserve between runs in state
    snapshots.
    
    The namedvals internal model isn't really designed to support enumerating
    all of the output values for a particular module call, but our expression
    evaluator currently depends on being able to do that and so we have a
    temporary inefficient implementation of that which just scans the entire
    table of values as a stopgap just to avoid this commit growing even larger
    than it already is. In a future commit we'll rework the evaluator to
    support the PartialEval mode and at the same time move the responsiblity
    for enumerating all of the output values into the evaluator itself, since
    it should be able to determine what it's expecting by analyzing the
    configuration rather than just by trusting that earlier evaluation has
    completed correctly.
    
    Because our legacy state string serialization previously included output
    values for all modules, some of our context tests were accidentally
    depending on the implementation detail of how those got stored internally.
    Those tests are updated here to test only the data that is a real part
    of Terraform Core's result, by ensuring that the relevant data appears
    somewhere either in a root output value or in a resource attribute.
    
    As of this commit, what remains of the states.State model can now be
    entirely serialized by the state snapshot format, with no more situations
    where we just silently drop some data that Terraform Core uses as an
    implementation detail.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    33e909e View commit details
    Browse the repository at this point in the history
  14. core: The expression evaluator has access to the instances.Expander

    This will allow it to determine which instances _should_ be present rather
    than just trusting which instances _are_ present, which will make it
    harder to accidentally hide graph ordering bugs behind fallback behavior
    and, more importantly, will allow the evaluator to recognize the
    difference between there being no instances at all or the instance keys
    not yet being known.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    c1593dc View commit details
    Browse the repository at this point in the history
  15. Configuration menu
    Copy the full SHA
    0d994a5 View commit details
    Browse the repository at this point in the history
  16. core: Expression evaluator can handle partial-eval in GetModule

    This is a totally different approach to GetModule which uses the
    configuration and previously-registered expansion to determine what ought
    to exist in our named values state, rather than treating the values in
    the named values state as the source of truth.
    
    As a result we get an overall simpler implementation which is able to
    panic if other components aren't behaving correctly, and we can return
    placeholder results in partial evaluation mode, at least as long as we're
    working with a single-instance module.
    
    There are some further opportunities for simplification and improving the
    detail of the unknown results if we make broader changes in future, but
    for the moment this is just enough to mimic the previous behavior using
    a new strategy.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    6f577d2 View commit details
    Browse the repository at this point in the history
  17. core: Beginnings of placeholders for resources with unknown expansion

    This doesn't actually do anything useful yet, but at least stubs out how
    evaluation for these might work in later commits.
    apparentlymart committed Nov 29, 2023
    Configuration menu
    Copy the full SHA
    14f54c5 View commit details
    Browse the repository at this point in the history