-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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): (Aspects are not processed on portions of the construct tree, when other aspects dynamically modify the tree (e.g. cdk pipelines)) #21341
Comments
After looking into this, I don't think this is a good idea. If we do multiple passes in the construct tree, nested aspects that were added in the first pass will be executed in the second pass. This is a behavior that we are explicitly ruling out. To understand the consequences of dropping this constraint, remember that every node in the tree inherits the aspects from all its ancestors. This means that nested aspects get replicated as you go down the tree. As an illustration, consider this scenario (taken from a unit test): const app = new App();
const root = new MyConstruct(app, 'MyConstruct');
const child = new MyConstruct(root, 'ChildConstruct');
Aspects.of(root).add({
visit(construct: IConstruct) {
Aspects.of(construct).add({
visit(inner: IConstruct) {
inner.node.addMetadata('test', 'would-be-ignored');
},
});
},
}); In the fist pass, the outer aspect adds the inner aspect to the root and to the child. Note that, to Node.js, these are two different objects. In the second pass, when the root is visited, the nested aspect will be executed once. When the child is visited, however, it will be executed twice (once for its own version of the nested aspect, and once for the nested aspect inherited from the root). If we add a grandchild, the aspect would be executed three times there, and so on. And this problem is two-dimensional. If you have an aspect that adds another aspect, that adds another aspect, you multiply the number of nested aspects by the level of the node in tree. @DaniloTommasinaTR If you have a suggestion to avoid this problem while allowing aspects to operate on new nodes, please let us know. |
Thanks for looking into this. This PR is not about removing the constraint, the following check stays as it was before: if (!nestedAspectWarning && nodeAspectsCount !== aspects.all.length) {
Annotations.of(construct).addWarning('We detected an Aspect was added via another Aspect, and will not be applied');
nestedAspectWarning = true;
} The check to avoid the same aspect being visited twice on the same node stays also as it was: let invoked = invokedByPath[node.path];
if (!invoked) {
invoked = invokedByPath[node.path] = [];
}
if (invoked.includes(aspect)) { continue; }
// ...
// mark as invoked for this node
invoked.push(aspect); What my code does, is just ensuring that any new nodes (and only those) added during one pass of the tree are being visited in the following pass. This is repeated as long as all nodes have been processed exactly once, respectively until no new nodes are processed in a pass. If the dynamically created nodes add nested aspects, then the warning will be created as it was before. I tested with your nested aspects and I do not see a difference in behavior with my fix, the warning is shown and nested aspects are not processed, I just see the aspects inherited from the parent nodes being processed as expected. |
@DaniloTommasinaTR the problem is not the warning. It's the fact that the warning will no longer be true. The nested aspects will be applied. Right now, nested aspects don't get applied because each node is visited only once. So even if an aspect creates another aspect, the nested one will simply be left behind and never executed. With your proposed change, these nested aspects will be picked up in the subsequent pass. Suppose you have an aspect
After the first pass, as a result of executing
Notice that In the second pass, |
@otaviomacedo ok I see now what you mean, thanks for the details. So my question here is, if Aspects adding other Aspects are not permitted, respectively do not produce any result other than a warning, why not throwing an error then instead of a warning? Do you expect any regression error with existing stacks? Maybe it could be also an option to allow disabling inheritance for Aspects as a general feature e.g. in the |
I ran into this issue as well and I agree with @DaniloTommasinaTR that I would almost expect an error from CDK in this case. Short of that, it might be worth an update to the documentation, unless I missed something that already exists. I was looking at using aspects to add a third party construct to nodes in a stack, but lost tags as a result. In this case because the third party used the Is it safe to conclude that it is inadvisable to use aspects to interact with third party constructs? I cannot control whether they use aspects themselves (either now or in the future), and if they do, I won't receive any functionality from them that was added via aspects. |
Came to a similar error where I wanted to tag certain resources by using a simbol so I am using aspects to check for such symbol, but then I would like to tag the node if is flagged
But get the same problem where it would not tag the resource, did you find another way @postsa ? |
The Taggging documentation includes the following example code that tries to tag constructs by an aspect:
It seems that this code does not work as of CDK However, I finally found a solution that directly uses the
|
@DaniloTommasinaTR have you validated that the enhanced |
It's a while back, I think my proposed fix was working on version 2.31.2, but we have now 2.162.1 and things might have changed... I re-checked the code and to me it seems it should work as expected, but I would need some time to setup the environment to test it again and I am pretty tied up at the moment. :( |
Can confirm that we are able to reproduce this issue. Looking into it |
@DaniloTommasinaTR we are writing an RFC to redesign how Aspects are applied in CDK. Would love your feedback/thoughts since you are the one who brought this issue to our attention! |
Hi @sumupitchayan |
Describe the bug
Aspects can modify the constructs' tree dynamically. The tree is modified when the Aspect is being visited.
If the modification happens in a higher node in the tree, then the aspects in this new branch are not processed, since the Aspects' processing recursion has already passed that level.
This is the case with CDK Pipelines, the "Source" stage is added in a higher location in the tree, causing tags and other aspects to not be added/processed for some elements of the dynamically generated tree.
In theory this can happen with other constructs that use a similar technique to dynamically extend the constructs' tree.
Expected Behavior
After synthesis the tree should look like
Current Behavior
After synthesis the tree looks like
Reproduction Steps
Init sample app with:
mkdir /tmp/cdk-issue cd /tmp/cdk-issue cdk init sample-app --language typescript
Replace content of
/tmp/cdk-issue/lib/cdk-issue-stack.ts
with the following:===> Tags should be present on both buckets, but tags are set only on fist bucket.
Possible Solution
In
aws-cdk-lib/core/lib/private/synthesis.ts
Enhance function
invokeAspects
as follows:The above addition will repeat the recursion on the full tree as long as any new invocation happens.
Additional Information/Context
This affects CDK Pipelines, tagging (and other Aspects) are not propagated to all stages, which is a key blocker within our company.
CDK CLI Version
2.31.2
Framework Version
No response
Node.js Version
v14.19.1
OS
All
Language
Typescript, Python, .NET, Java, Go
Language Version
all
Other information
No response
The text was updated successfully, but these errors were encountered: