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

Module Expansion Activate! #24461

Merged
merged 15 commits into from
Mar 26, 2020
Merged

Module Expansion Activate! #24461

merged 15 commits into from
Mar 26, 2020

Conversation

jbardin
Copy link
Member

@jbardin jbardin commented Mar 25, 2020

This turns on module expansion in the config, with most basic functionality working. A major exception is that while providers cannot be configured within an expanded module, there is no validation for that yet and the results are going to be undefined.

There are some UnkeyedInstanceShim calls are left in the abstract provider, because providers need to be evaluated before expansion happens. This is OK, since we will pre-validate that providers cannot be configured within expanding modules. There is also a call in resource validation, because we need to create a context within which we can validate, but there is no expansion to create module instances. This type of usage of the UnkeyedInstanceShim will likely remain for the time being.

To link the bulk of the work together here, the series of PRs directly leading to this is #24154, #24296, #24331, #24346, #24362, #24389, #24444, #24454.

Other key points in this PR:

Add EvalContext.WithPath

As the Graph is walked, the current way to set the context path was to
have the walker return a context from EnterPath. This required that
every node know it's absolute path, which can no longer be the case
when modules have not been expanded.

This introduces a new method called WithPath, which returns a copy of
the context with the internal path updated to reflect the method
argument. Any use of the EvalContext that requires knowing the path will
now panic if it wasn't explicitly set to ensure that evaluations always occur
in the correct path. While this may be beneficial for development, this
should be converted to a contextual error later on, so in the rare event that
we evaluate an unknown path the user can report the error in a useful way.

Add EvalContext to the GraphWalker interface.

EvalContext returns an EvalContext that has not yet set a path. This
will allow us to enforce that all context operations requiring a module
instance path will require that a path be explicitly set rather than
evaluating within the wrong path.

Add separate expand nodes to all Resource nodes that will need expansion.

While the Expander itself now handles the recursive expansion of
modules, Resources still need to be expanded twice, because
the evaluation of the Resource, which entails evaluating the for_each or
count expressions, is separate from the ResourceInstance expansion.
Adding expander nodes ensures that the individual eval nodes can be
created with the correct path for evaluation.

Track dependencies via addr.ConfigResource

We don't have access to module instances during the full graph transformations,
so we can't record these in the state. Using addrs.ConfigResource won't change
the current behavior, but makes the dependencies tracking less precise when
instances are connected to resources in other modules. We may likely revisit
this once we can ensure Refresh no longer needs to evaluate Resource
configuration.

Implements #17519, but leaving open until we are more feature complete.

@ghost ghost added the sdkv1 [PRs only] Marks changes that may potentially need to be ported to the plugi nSDK label Mar 25, 2020
@jbardin jbardin requested a review from a team March 25, 2020 20:13
As the Graph is walked, the current way to set the context path was to
have the walker return a context from EnterPath. This required that
every node know it's absolute path, which can no longer be the case
during plan when modules have not been expanded.

This introduces a new method called WithPath, which returns a copy of
the context with the internal path updated to reflect the method
argument. Any use of the EvalContext that requires knowing the path will
now panic if it wasn't explicitly set to ensure that evaluations always
occur in the correct path.

Add EvalContext to the GraphWalker interface.
EvalContext returns an EvalContext that has not yet set a path. This
will allow us to enforce that all context operations requiring a module
instance path will require that a path be explicitly set rather than
evaluating within the wrong path.
While the Expander itself now handles the recursive expansion of
modules, Resources themselves still need to be expanded twice, because
the evaluation of the Resource, which entails evaluating the for_each or
count expressions, is separate from the ResourceInstance expansion.

Add a nodeExpandPlannableResource to do handle this expansion to allow
all NodePlannableResources to call EvalWriteResourceState with an
absolute address.
Resources also need to be expanded during apply, which cannot be done
via EvalTree due to the lack of EvalContext.
Use the new addrs type here.

Also remove the uniqueMap from the config transformer. We enforce
uniqueness during config loading, and this is more likely to have false
positives due to stringification than anything.
We can't get module instances during transformation, so we need to
reduce the Dependencies to using `addrs.ConfigResource` for now.
Remove the shims where they aren't necessary from the Init and Close
provider nodes. This also removed some provider path checks from the
builtin eval context, which cannot be resolved since the context may not
be created with a ModuleInstance path.
The expand logic was separated into
nodeExpandRefreshableManagedResource, but the orphan logic wasn't
updated.
simplify the test a bit and add a few more combinations to the config
eval nodes no longer always have a context path
case forEachMap != nil:
expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachMap)
default:
expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: what is the expected behavior when SetResourceSingle is called when count = 0 ?

Copy link
Contributor

@mildwonkey mildwonkey Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah! I found the answer in a comment just below this (and was misreading the actual condition, still on my first ☕️):

// -1 signals "count not set"

Copy link
Member Author

@jbardin jbardin Mar 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, count = 0 is still in list-mode, just with 0 instances.

Copy link
Contributor

@mildwonkey mildwonkey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me, though I suggest you get a second review with someone with more context :)

Copy link
Contributor

@pselle pselle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly 🎉 and a comment about where comment might be improved/more understanding left for Future Readers


m := testModule(t, "plan-modules-count")
func TestContext2Plan_moduleExpand(t *testing.T) {
// Test a smattering of plan expansion behavior
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smattering!

if countDiags.HasErrors() {
return nil, countDiags.Err()
}
// nodeExpandModule itself does not have visibility into how its ancestors
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

helpful comment!

// NewNodeAbstractResource creates an abstract resource graph node for
// the given absolute resource address.
func NewNodeAbstractResource(addr addrs.AbsResource) *NodeAbstractResource {
// FIXME: this should probably accept a ConfigResource
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now it does!

// nodeExpandApplyableResource handles the first layer of resource
// expansion during apply. This is required because EvalTree does not have a
// context with which to expand the resource into multiple instances.
// This type should be a drop in replacement for NodeApplyableResource, and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a "drop-in replacement" is the implication that it is used instead of NodeApplyableResource in certain scenarios, but not all? And what are those scenarios? (something that might be helpful to clarify here)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point, I do that too often in comments. I've already done the replacement, and the comment should only explain the "why"

@@ -100,12 +100,7 @@ func (n *NodeApplyableResourceInstance) References() []*addrs.Reference {

// GraphNodeAttachDependencies
func (n *NodeApplyableResourceInstance) AttachDependencies(deps []addrs.ConfigResource) {
var shimmed []addrs.AbsResource
for _, r := range deps {
shimmed = append(shimmed, r.Absolute(r.Module.UnkeyedInstanceShim()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

This name method won't be called in the full graph, and remove it to
prevent confusion with the parent node in logs.
@jbardin jbardin merged commit 34cab3b into master Mar 26, 2020
@jbardin jbardin deleted the jbardin/eval-context-path branch March 26, 2020 16:45
@n10000k
Copy link

n10000k commented Apr 26, 2020

Waiting on this bad boy, in need of for_each on modules urgently </3 going to save so much time and can just pass an object of microservices for lambda and let it all take cause and build up like magic. <3 Keep up the good work everyone.

@ghost
Copy link

ghost commented Apr 26, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@ghost ghost locked and limited conversation to collaborators Apr 26, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
sdkv1 [PRs only] Marks changes that may potentially need to be ported to the plugi nSDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants