-
Notifications
You must be signed in to change notification settings - Fork 4k
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
fix(cli): metadata not recorded for templates >50k #10184
Conversation
The CDK Metadata Resource is added by the CLI, and only added to the in-memory representation of the template. This in-memory representation is only used in the `CreateChangeSet` call if the full template body is <=50kB. If the template is larger than this size, the template file that's on disk will be uploaded to the bootstrap S3 bucket and referenced. However, that template will not have had the metadata resource injected and so the stack will not be registered. This PR: - Provisionally fixes this issue by flushing the in-memory modifications back out to disk. - Also adds the metadata resources to stacks found in nested Cloud Assemblies, such as the stacks deployed via CDK Pipelines. There is some jank around new-style synthesis stacks: the protocol specifies that those stacks have synthesized their templates as assets, and those will not (necessarily) be processed by the CLI, although it might coincidentally work if the files are the same. The proper fix to this whole mess is to have the framework inject the metadata resource at synthesis time. That fix is forthcoming, but this current one is a good stopgap until that time.
// @deprecated(v2): this should honestly not be done here. The framework | ||
// should (and will, shortly) synthesize this information directly into | ||
// the template. However, in order to support old framework versions | ||
// that don't synthesize this info yet, we can only remove this code | ||
// once we break backwards compatibility. | ||
await this.addMetadataResource(assembly); |
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.
Can we be super clear about what should happen as part of v2?
Should this if
statement be dropped entirely? I'd like the engineer who is doing the v2 work not have to load a ton of context.
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.
should (and will, shortly) synthesize this information directly into the template
I assume this has not bee done yet. Let's wait until so before we can add the @deprecated
code.
We should be using the @deprecated
annotation only when it's ready to be dropped.
Sorry to be a pita.
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.
requesting changes for the comment around @deprecated
tag.
private async addMetadataResource(rootAssembly: cxapi.CloudAssembly) { | ||
if (!rootAssembly.runtime) { return; } | ||
|
||
const modules = formatModules(rootAssembly.runtime); | ||
await processAssembly(rootAssembly); | ||
|
||
function formatModules(runtime: cxapi.RuntimeInfo): string { | ||
const modules = new Array<string>(); | ||
async function processAssembly(assembly: cxapi.CloudAssembly) { | ||
for (const stack of assembly.stacks) { | ||
await processStack(stack); | ||
} | ||
for (const nested of assembly.nestedAssemblies) { | ||
await processAssembly(nested.nestedAssembly); | ||
} | ||
} | ||
|
||
// inject toolkit version to list of modules | ||
// eslint-disable-next-line @typescript-eslint/no-require-imports | ||
const toolkitVersion = require('../../../package.json').version; | ||
modules.push(`aws-cdk=${toolkitVersion}`); | ||
async function processStack(stack: cxapi.CloudFormationStackArtifact) { | ||
const resourcePresent = stack.environment.region === cxapi.UNKNOWN_REGION | ||
|| RegionInfo.get(stack.environment.region).cdkMetadataResourceAvailable; | ||
if (!resourcePresent) { return; } | ||
|
||
for (const key of Object.keys(runtime.libraries).sort()) { | ||
modules.push(`${key}=${runtime.libraries[key]}`); | ||
if (!stack.template.Resources) { | ||
stack.template.Resources = {}; | ||
} | ||
if (stack.template.Resources.CDKMetadata) { | ||
warning(`The stack ${stack.id} already includes a CDKMetadata resource`); | ||
return; | ||
} | ||
|
||
stack.template.Resources.CDKMetadata = { | ||
Type: 'AWS::CDK::Metadata', | ||
Properties: { | ||
Modules: modules, | ||
}, | ||
}; | ||
|
||
if (stack.environment.region === cxapi.UNKNOWN_REGION) { | ||
stack.template.Conditions = stack.template.Conditions || {}; | ||
const condName = 'CDKMetadataAvailable'; | ||
if (!stack.template.Conditions[condName]) { | ||
stack.template.Conditions[condName] = _makeCdkMetadataAvailableCondition(); | ||
stack.template.Resources.CDKMetadata.Condition = condName; | ||
} else { | ||
warning(`The stack ${stack.id} already includes a ${condName} condition`); | ||
} | ||
return modules.join(','); | ||
} |
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.
Has any logic changed here at all or is this all refactor? I hate non-trivial refactors in bug fix PRs.
I can't say if I should or not should not scrutinize this code.
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.
I know, sorry. I was cleaning this up and then detected the bug. It shouldn't be fairly easy to parse once you know how to chunk it.
The part that injects the resource is the same (processStack
is an extract method refactor), the path leading up to it (processAssembly
) is different: we now recurse through assemblies.
// The template has changed in-memory, but the file on disk remains unchanged so far. | ||
// The CLI *might* later on deploy the in-memory version (if it's <50kB) or use the | ||
// on-disk version (if it's >50kB). | ||
// | ||
// Be sure to flush the changes we just made back to disk. The on-disk format is always | ||
// JSON. | ||
await fs.writeFile(stack.templateFullPath, JSON.stringify(stack.template, undefined, 2), { encoding: 'utf-8' }); |
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.
Mutate the synthesized template. Ewww...
I suppose the root problem is that the CLI shouldn't be injecting the metadata. Is it safe to assume this will not happen when the correct fix in put in place?
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.
Wouldn't be much easier to simply move metadata collection to the framework now instead of more tweaks in the CLI?
Clarified that the
Yes, but not good enough. Doesn't help for old framework version -- new versions of the CLI should basically still support I'll also direct your attention to the closing paragraph of the PR:
|
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
// @deprecated(v2): remove this 'if' block and all code referenced by it. | ||
// This should honestly not be done here. The framework | ||
// should (and will, shortly) synthesize this information directly into | ||
// the template. However, in order to support old framework versions | ||
// that don't synthesize this info yet, we can only remove this code | ||
// once we break backwards compatibility. |
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.
I still think we shouldn't add this notice until the change is done to the framework
Hello @rix0rrr ! When I run
I'm using the the Is there something I can do to remove the warning? Sorry if this isn't a good place to ask this question. |
The CDK Metadata Resource is added by the CLI, and used to be only added
to the in-memory representation of the template.
This in-memory representation is only used in the
CreateChangeSet
call if the full template body is <=50kB.
If the template is larger than this size, the template file that's on
disk will be uploaded to the bootstrap S3 bucket and referenced.
However, that template will not have had the metadata resource
injected and so the stack will not be registered.
This broken behavior was introduced in v1.29.0, released in March 2020,
when we switched to uploading the on-disk file to S3 instead of a re-serialization
of the [modified] in-memory representation. See:
a75f711
This PR:
back out to disk.
Assemblies, such as the stacks deployed via CDK Pipelines.
There is some jank around new-style synthesis stacks: the protocol
specifies that those stacks have synthesized their templates as assets,
and those will not (necessarily) be processed by the CLI, although
it might coincidentally work if the files are the same.
The proper fix to this whole mess is to have the framework inject the
metadata resource at synthesis time. That fix is forthcoming, but this
current one is a good stopgap until that time.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license