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

feat(core): CfnResource depends on methods #20419

Closed
wants to merge 23 commits into from

Conversation

jusdino
Copy link
Contributor

@jusdino jusdino commented May 19, 2022

Add some new methods to allow a minimal interface for viewing and editing resource-to-resource dependencies that mirrors the behavior of CfnResource.addDependsOn().

Related to #20418 - details for justification are there


All Submissions:

  • Have you followed the guidelines in our Contributing guide?
  • This PR adds new unconventional dependencies following the process described here
  • Have you added the new feature to an integration test?
    • Did you use yarn integ to deploy the infrastructure and generate the snapshot (i.e. yarn integ without --dry-run)?

Adding new Unconventional Dependencies:

New Features

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@gitpod-io
Copy link

gitpod-io bot commented May 19, 2022

@aws-cdk-automation aws-cdk-automation requested a review from a team May 19, 2022 16:43
@github-actions github-actions bot added the p2 label May 19, 2022
@TheRealAmazonKendra TheRealAmazonKendra changed the base branch from v1-main to main June 2, 2022 09:17
Copy link
Contributor

@comcalvi comcalvi left a comment

Choose a reason for hiding this comment

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

I like the additions this makes. However, if the intended use case is for users to write

foo = stack.tryFindChild('Foo');
if (foo.obtainDependsOn().contains(bar)) {
  foo.removeDependsOn(bar);
fi
foo.addDependsOn(barReplacement);

we should also add a replaceDependsOn(bar, barReplacement) that does exactly what the above snippet does.

packages/@aws-cdk/core/test/cfn-resource.test.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/test/cfn-resource.test.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/test/cfn-resource.test.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/cfn-resource.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/deps.ts Show resolved Hide resolved
packages/@aws-cdk/core/lib/deps.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/deps.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/deps.ts Outdated Show resolved Hide resolved
function resourceInCommonStackFor(element: CfnResource | Stack, commonStack: Stack): CfnResource {
const resource = Stack.isStack(element) ? element.nestedStackResource : element;
if (!resource) {
throw new Error('assertion failure'); // see "assertion" above
Copy link
Contributor

Choose a reason for hiding this comment

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

this is not a very descriptive error message.

packages/@aws-cdk/core/lib/stack.ts Outdated Show resolved Hide resolved
@mergify mergify bot dismissed comcalvi’s stale review June 16, 2022 14:51

Pull request has been modified.

@jusdino jusdino requested a review from comcalvi June 30, 2022 16:03
packages/@aws-cdk/core/lib/deps.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/cfn-resource.ts Show resolved Hide resolved
packages/@aws-cdk/core/lib/cfn-resource.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/cfn-resource.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/cfn-resource.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/stack.ts Outdated Show resolved Hide resolved
let index;
let filteredReasons = filterReasons(dep, reasonFilter);
if (filteredReasons.length > 1) {
throw new Error(`Reason for dependency removal is too ambiguous: ${reasonFilter}`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
throw new Error(`Reason for dependency removal is too ambiguous: ${reasonFilter}`);
throw new Error(`There cannot be more than one reason for dependency removal, found: ${reasonFilter}`);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Took your message but swapped /reasonFilter/filteredReasons/ since I think the latter makes more sense with your message.

packages/@aws-cdk/core/test/cfn-resource.test.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/stack.ts Show resolved Hide resolved
@mergify mergify bot dismissed comcalvi’s stale review July 18, 2022 21:15

Pull request has been modified.

@jusdino
Copy link
Contributor Author

jusdino commented Jul 26, 2022

@comcalvi, based on what I learned in #20281, I think we'll eventually have to reopen this pull request from a personal fork of aws-cdk instead of this organization-owned fork, since your mergify bot cannot push changes back to this fork branch. If you're close to satisfied with this PR, let me know and I'll do that. I didn't want to do that before we were nearly there, since I didn't want to break up our workflow while we still had revisions to work out.

Copy link
Contributor

@TheRealAmazonKendra TheRealAmazonKendra left a comment

Choose a reason for hiding this comment

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

These test failures have to do with a bug we had when you last merged from main. Please rebase so we can see if the build is succeeding before we can provide another round of reviews.

@mergify mergify bot dismissed TheRealAmazonKendra’s stale review August 3, 2022 00:22

Pull request has been modified.

@TheRealAmazonKendra
Copy link
Contributor

@jusdino Go into the aws-cdk-lib package and run yarn pkglint. Commit the change to the README file for aws-cdk-lib.

Copy link
Contributor

@TheRealAmazonKendra TheRealAmazonKendra left a comment

Choose a reason for hiding this comment

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

@comcalvi is already providing great feedback but I just want to add that I think this needs more testing. I'd like to see both more unit test coverage for these changes and integration tests as well. If you have any questions about writing integration tests, see our integration test guide.

packages/@aws-cdk/core/lib/stack.ts Outdated Show resolved Hide resolved
@mergify mergify bot dismissed TheRealAmazonKendra’s stale review August 8, 2022 22:58

Pull request has been modified.

@jusdino
Copy link
Contributor Author

jusdino commented Aug 8, 2022

Ok, I detected the Names.uniqueId bug in an existing unit test and added a two-part integration test. I can look into adding some more unit tests for further coverage next.

packages/@aws-cdk/core/lib/stack.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/cfn-resource.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/deps.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/stack.ts Outdated Show resolved Hide resolved
packages/@aws-cdk/core/lib/stack.ts Outdated Show resolved Hide resolved
@jusdino
Copy link
Contributor Author

jusdino commented Nov 28, 2022

@comcalvi ?

@comcalvi
Copy link
Contributor

comcalvi commented Nov 29, 2022

I don't see any breaking changes here - what are you referring to? The closest thing I see is the addDependsOn -> addDependency deprecation, which I did specifically as a change requested by Amazon. I'm happy to roll that back, if that is ultimately what you want.

I missed that the method whose signature changed was internal, that's my mistake.

Traversing the construct tree to remove dependencies is exactly what this enables. With the existing code, there is no mechanism to do that. I'm not sure what your point is here. Can you help me understand?

Sorry I wasn't more clear; I do not understand this mechanism. The main thing I'm missing the usage of filterReasons(). What does this function do? Why do we need to filter any reasons at all? I see filterReasons() used in a few spots, and I don't see what this is doing.

Ok, I'm trying to boil down where my approach differs from what you're suggesting, since I don't think it's far from what I did. Aside from adding _removeAssemblyDependency() and removeDependency() and adding the helper methods to pull out the common code, I also:

The method additions and name changes are good, this comment was about the new mechanism. A comment block detailing this mechanism would be helpful. I'm not understanding what precise capability this is adding.

@jusdino
Copy link
Contributor Author

jusdino commented Nov 29, 2022

Sorry I wasn't more clear; I do not understand this mechanism. The main thing I'm missing the usage of filterReasons(). What does this function do? Why do we need to filter any reasons at all? I see filterReasons() used in a few spots, and I don't see what this is doing.

Ah ok. So the filterReasons function is the core of what I'm explaining here:
#20419 (comment)

Would adding a comment block with something along those lines be helpful?

@comcalvi
Copy link
Contributor

the method will resolve it to a stack-level dependency and, rather than remove the dependency, it will find the matching source and target in the StackDependencyReason, remove that reason, but leave the stack dependency in place, since it will still have the source: ResourceB, target: ResourceD dependency reason. If the user code also then does ResourceB.removeDependsOn(ResourceD), the method will again resolve to the stack level, remove the corresponding StackDependencyReason then, seeing that there is no longer a reason for the dependency, remove the Stack1 on Stack2 dependency as well.

I don't see the purpose of filtering reasons in this process though. From this, I understand that we need to track resource dependencies across stacks. I think today we don't do this, and instead only track the stack itself as a dependency; this is a problem. As you explain, if we only track the stack as a dependency and a user removes one dependency but not the other, we can't determine that the stack dependency should actually be left in place; therefore, we need a mechanism to track the resources themselves as dependencies and not just the stack.

With that in mind, I would expect the fundamental change to add the construct path to the dependency list, eg Stack2/Resource (where today, it's just Stack2). To achieve that, you're tracking source and target for each dependency. That makes sense. What doesn't make sense to me is why we need to filter the dependency reasons. What problem does filtering dependencies sovle?

@jusdino
Copy link
Contributor Author

jusdino commented Dec 5, 2022

What doesn't make sense to me is why we need to filter the dependency reasons. What problem does filtering dependencies sovle?

Sorry for the slow reply - I've been sick and wanted to think this through. My short answer is, looking closer, the filterReasons may be a bit overkill.

What we really need is to be able to match a StackDependencyReason reliably when removing resource-to-resource dependencies that ended up resolving as stack-to-stack dependencies. The filterReasons function is doing this for us but it may be a bit more complex than necessary specifically because I was trying to be a bit defensive in my approach. I was specifically thinking about the situation where, somehow, a source or target of null or undefined was added to the stackDependency.reasons. I don't think the public interface allows such a thing to happen, but if it did, I wanted a smarter approach to pulling out a match than specifically looking for source == reason.source && target == reason.target. Otherwise, that reason would never match and so would prevent the StackDependency from ever being completely removed.

It sounds like your preference would be to simplify. I could simplify/remove the filterReasons function if you would rather leave that null/undefined unaddressed, which I think is probably fine since it shouldn't happen, anyway.

@comcalvi
Copy link
Contributor

comcalvi commented Dec 5, 2022

Thanks for the explanation, this makes sense. It's good to think of edge cases like this, and we should definitely guard against it. However I'd prefer to fail in this case, since the API shouldn't allow this to happen. Even it happens to be impossible for this case to occur with today's code, future changes may have bugs that create a null or undefined reason that could be silently ignored if we just filter out those reasons. I'd prefer we throw an error if we encounter a null or undefined reason and remove the filterReasons() method entirely, since such a reason existing means that something has gone wrong and the deployment may fail (or worse yet, deploy successfully by chance but with missing dependency logic which may cause it to fail for seemingly no reason in the future).

@jusdino
Copy link
Contributor Author

jusdino commented Dec 5, 2022

Thanks for the explanation, this makes sense. It's good to think of edge cases like this, and we should definitely guard against it. However I'd prefer to fail in this case, since the API shouldn't allow this to happen. Even it happens to be impossible for this case to occur with today's code, future changes may have bugs that create a null or undefined reason that could be silently ignored if we just filter out those reasons. I'd prefer we throw an error if we encounter a null or undefined reason and remove the filterReasons() method entirely, since such a reason existing means that something has gone wrong and the deployment may fail (or worse yet, deploy successfully by chance but with missing dependency logic which may cause it to fail for seemingly no reason in the future).

Sounds good. I'll work on that next along with the smaller changes from your last review.

@mergify mergify bot dismissed comcalvi’s stale review December 7, 2022 02:02

Pull request has been modified.

@jusdino
Copy link
Contributor Author

jusdino commented Dec 7, 2022

@comcalvi, @MrArnoldPalmer, I think this covers everything except for what to do about IElement - just need some direction on which way to go with that.

@comcalvi
Copy link
Contributor

comcalvi commented Dec 7, 2022

for IElement, please leave the original union type Element as it was.

@comcalvi
Copy link
Contributor

@jusdino Can you please enable contributions by maintainers to your fork? I have a commit I'd like to push to this PR, and then I'll be happy to approve it. Thanks for all your hard work on this PR, this is in a really good spot now!

@jusdino
Copy link
Contributor Author

jusdino commented Dec 10, 2022

@jusdino Can you please enable contributions by maintainers to your fork? I have a commit I'd like to push to this PR, and then I'll be happy to approve it. Thanks for all your hard work on this PR, this is in a really good spot now!

Glad to be close to the finish line!

So this PR is from a fork on a GitHub organization and I wasn’t able to find a way to do that with my last PR from there. Last time, I ended up reopening a new PR from a personal fork. I can do that here as well though it will take me a bit - got some personal stuff going on atm.

For reference:
#20419 (comment)

@comcalvi
Copy link
Contributor

Thanks! Just link it here and I'll approve once I push a commit to it.

@jusdino
Copy link
Contributor Author

jusdino commented Dec 17, 2022

@comcalvi , #23383 is ready 👍

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildv2Project1C6BFA3F-wQm2hXv2jqQv
  • Commit ID: 6ed2097
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@comcalvi
Copy link
Contributor

Closing this one in favor of #23383

@comcalvi comcalvi closed this Dec 19, 2022
mergify bot pushed a commit that referenced this pull request Dec 19, 2022
Reopening #20419 from a personal fork to allow maintainer edits. @comcalvi 👋 

Add some new methods to allow a minimal interface for viewing and editing resource-to-resource dependencies that mirrors the behavior of CfnResource.addDependsOn().

Related to #20418 - details for justification are there

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Construct Runtime Dependencies:

* [ ] This PR adds new construct runtime dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-construct-runtime-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
brennanho pushed a commit to brennanho/aws-cdk that referenced this pull request Jan 20, 2023
Reopening aws#20419 from a personal fork to allow maintainer edits. @comcalvi 👋 

Add some new methods to allow a minimal interface for viewing and editing resource-to-resource dependencies that mirrors the behavior of CfnResource.addDependsOn().

Related to aws#20418 - details for justification are there

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Construct Runtime Dependencies:

* [ ] This PR adds new construct runtime dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-construct-runtime-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
brennanho pushed a commit to brennanho/aws-cdk that referenced this pull request Feb 22, 2023
Reopening aws#20419 from a personal fork to allow maintainer edits. @comcalvi 👋 

Add some new methods to allow a minimal interface for viewing and editing resource-to-resource dependencies that mirrors the behavior of CfnResource.addDependsOn().

Related to aws#20418 - details for justification are there

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md)

### Adding new Construct Runtime Dependencies:

* [ ] This PR adds new construct runtime dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-construct-runtime-dependencies)

### New Features

* [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)?
	* [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)?

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/core Related to core CDK functionality p2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants