@aws-cdk/core
Version:
AWS Cloud Development Kit Core Library
108 lines • 13.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.assertBound = exports.contentHash = exports.addStackArtifactToAssembly = void 0;
const crypto = require("crypto");
const cxschema = require("@aws-cdk/cloud-assembly-schema");
const construct_compat_1 = require("../construct-compat");
const stack_1 = require("../stack");
/**
* Shared logic of writing stack artifact to the Cloud Assembly
*
* This logic is shared between StackSyntheses.
*
* It could have been a protected method on a base class, but it
* uses `Partial<cxapi.AwsCloudFormationStackProperties>` in the
* parameters (which is convenient so I can remain typesafe without
* copy/pasting), and jsii will choke on this type.
*/
function addStackArtifactToAssembly(session, stack, stackProps, additionalStackDependencies) {
// nested stack tags are applied at the AWS::CloudFormation::Stack resource
// level and are not needed in the cloud assembly.
if (stack.tags.hasTags()) {
stack.node.addMetadata(cxschema.ArtifactMetadataEntryType.STACK_TAGS, stack.tags.renderTags());
}
const deps = [
...stack.dependencies.map(s => s.artifactId),
...additionalStackDependencies,
];
const meta = collectStackMetadata(stack);
// backwards compatibility since originally artifact ID was always equal to
// stack name the stackName attribute is optional and if it is not specified
// the CLI will use the artifact ID as the stack name. we *could have*
// always put the stack name here but wanted to minimize the risk around
// changes to the assembly manifest. so this means that as long as stack
// name and artifact ID are the same, the cloud assembly manifest will not
// change.
const stackNameProperty = stack.stackName === stack.artifactId
? {}
: { stackName: stack.stackName };
const properties = {
templateFile: stack.templateFile,
terminationProtection: stack.terminationProtection,
tags: nonEmptyDict(stack.tags.tagValues()),
...stackProps,
...stackNameProperty,
};
// add an artifact that represents this stack
session.assembly.addArtifact(stack.artifactId, {
type: cxschema.ArtifactType.AWS_CLOUDFORMATION_STACK,
environment: stack.environment,
properties,
dependencies: deps.length > 0 ? deps : undefined,
metadata: Object.keys(meta).length > 0 ? meta : undefined,
});
}
exports.addStackArtifactToAssembly = addStackArtifactToAssembly;
/**
* Collect the metadata from a stack
*/
function collectStackMetadata(stack) {
const output = {};
visit(stack);
return output;
function visit(node) {
// break off if we reached a node that is not a child of this stack
const parent = findParentStack(node);
if (parent !== stack) {
return;
}
if (node.node.metadata.length > 0) {
// Make the path absolute
output[construct_compat_1.ConstructNode.PATH_SEP + node.node.path] = node.node.metadata.map(md => stack.resolve(md));
}
for (const child of node.node.children) {
visit(child);
}
}
function findParentStack(node) {
if (node instanceof stack_1.Stack && node.nestedStackParent === undefined) {
return node;
}
if (!node.node.scope) {
return undefined;
}
return findParentStack(node.node.scope);
}
}
/**
* Hash a string
*/
function contentHash(content) {
return crypto.createHash('sha256').update(content).digest('hex');
}
exports.contentHash = contentHash;
/**
* Throw an error message about binding() if we don't have a value for x.
*
* This replaces the ! assertions we would need everywhere otherwise.
*/
function assertBound(x) {
if (x === null && x === undefined) {
throw new Error('You must call bindStack() first');
}
}
exports.assertBound = assertBound;
function nonEmptyDict(xs) {
return Object.keys(xs).length > 0 ? xs : undefined;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiX3NoYXJlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIl9zaGFyZWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUNBQWlDO0FBQ2pDLDJEQUEyRDtBQUMzRCwwREFBbUY7QUFDbkYsb0NBQWlDO0FBRWpDOzs7Ozs7Ozs7R0FTRztBQUNILFNBQWdCLDBCQUEwQixDQUN4QyxPQUEwQixFQUMxQixLQUFZLEVBQ1osVUFBOEQsRUFDOUQsMkJBQXFDO0lBRXJDLDJFQUEyRTtJQUMzRSxrREFBa0Q7SUFDbEQsSUFBSSxLQUFLLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxFQUFFO1FBQ3hCLEtBQUssQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyx5QkFBeUIsQ0FBQyxVQUFVLEVBQUUsS0FBSyxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQyxDQUFDO0tBQ2hHO0lBRUQsTUFBTSxJQUFJLEdBQUc7UUFDWCxHQUFHLEtBQUssQ0FBQyxZQUFZLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsQ0FBQztRQUM1QyxHQUFHLDJCQUEyQjtLQUMvQixDQUFDO0lBQ0YsTUFBTSxJQUFJLEdBQUcsb0JBQW9CLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFekMsMkVBQTJFO0lBQzNFLDRFQUE0RTtJQUM1RSxzRUFBc0U7SUFDdEUsd0VBQXdFO0lBQ3hFLHdFQUF3RTtJQUN4RSwwRUFBMEU7SUFDMUUsVUFBVTtJQUNWLE1BQU0saUJBQWlCLEdBQUcsS0FBSyxDQUFDLFNBQVMsS0FBSyxLQUFLLENBQUMsVUFBVTtRQUM1RCxDQUFDLENBQUMsRUFBRztRQUNMLENBQUMsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLENBQUMsU0FBUyxFQUFFLENBQUM7SUFFbkMsTUFBTSxVQUFVLEdBQThDO1FBQzVELFlBQVksRUFBRSxLQUFLLENBQUMsWUFBWTtRQUNoQyxxQkFBcUIsRUFBRSxLQUFLLENBQUMs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UyxDQUFDO0FBQ3JELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJztcbmltcG9ydCAqIGFzIGN4c2NoZW1hIGZyb20gJ0Bhd3MtY2RrL2Nsb3VkLWFzc2VtYmx5LXNjaGVtYSc7XG5pbXBvcnQgeyBDb25zdHJ1Y3ROb2RlLCBJQ29uc3RydWN0LCBJU3ludGhlc2lzU2Vzc2lvbiB9IGZyb20gJy4uL2NvbnN0cnVjdC1jb21wYXQnO1xuaW1wb3J0IHsgU3RhY2sgfSBmcm9tICcuLi9zdGFjayc7XG5cbi8qKlxuICogU2hhcmVkIGxvZ2ljIG9mIHdyaXRpbmcgc3RhY2sgYXJ0aWZhY3QgdG8gdGhlIENsb3VkIEFzc2VtYmx5XG4gKlxuICogVGhpcyBsb2dpYyBpcyBzaGFyZWQgYmV0d2VlbiBTdGFja1N5bnRoZXNlcy5cbiAqXG4gKiBJdCBjb3VsZCBoYXZlIGJlZW4gYSBwcm90ZWN0ZWQgbWV0aG9kIG9uIGEgYmFzZSBjbGFzcywgYnV0IGl0XG4gKiB1c2VzIGBQYXJ0aWFsPGN4YXBpLkF3c0Nsb3VkRm9ybWF0aW9uU3RhY2tQcm9wZXJ0aWVzPmAgaW4gdGhlXG4gKiBwYXJhbWV0ZXJzICh3aGljaCBpcyBjb252ZW5pZW50IHNvIEkgY2FuIHJlbWFpbiB0eXBlc2FmZSB3aXRob3V0XG4gKiBjb3B5L3Bhc3RpbmcpLCBhbmQganNpaSB3aWxsIGNob2tlIG9uIHRoaXMgdHlwZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFkZFN0YWNrQXJ0aWZhY3RUb0Fzc2VtYmx5KFxuICBzZXNzaW9uOiBJU3ludGhlc2lzU2Vzc2lvbixcbiAgc3RhY2s6IFN0YWNrLFxuICBzdGFja1Byb3BzOiBQYXJ0aWFsPGN4c2NoZW1hLkF3c0Nsb3VkRm9ybWF0aW9uU3RhY2tQcm9wZXJ0aWVzPixcbiAgYWRkaXRpb25hbFN0YWNrRGVwZW5kZW5jaWVzOiBzdHJpbmdbXSkge1xuXG4gIC8vIG5lc3RlZCBzdGFjayB0YWdzIGFyZSBhcHBsaWVkIGF0IHRoZSBBV1M6OkNsb3VkRm9ybWF0aW9uOjpTdGFjayByZXNvdXJjZVxuICAvLyBsZXZlbCBhbmQgYXJlIG5vdCBuZWVkZWQgaW4gdGhlIGNsb3VkIGFzc2VtYmx5LlxuICBpZiAoc3RhY2sudGFncy5oYXNUYWdzKCkpIHtcbiAgICBzdGFjay5ub2RlLmFkZE1ldGFkYXRhKGN4c2NoZW1hLkFydGlmYWN0TWV0YWRhdGFFbnRyeVR5cGUuU1RBQ0tfVEFHUywgc3RhY2sudGFncy5yZW5kZXJUYWdzKCkpO1xuICB9XG5cbiAgY29uc3QgZGVwcyA9IFtcbiAgICAuLi5zdGFjay5kZXBlbmRlbmNpZXMubWFwKHMgPT4gcy5hcnRpZmFjdElkKSxcbiAgICAuLi5hZGRpdGlvbmFsU3RhY2tEZXBlbmRlbmNpZXMsXG4gIF07XG4gIGNvbnN0IG1ldGEgPSBjb2xsZWN0U3RhY2tNZXRhZGF0YShzdGFjayk7XG5cbiAgLy8gYmFja3dhcmRzIGNvbXBhdGliaWxpdHkgc2luY2Ugb3JpZ2luYWxseSBhcnRpZmFjdCBJRCB3YXMgYWx3YXlzIGVxdWFsIHRvXG4gIC8vIHN0YWNrIG5hbWUgdGhlIHN0YWNrTmFtZSBhdHRyaWJ1dGUgaXMgb3B0aW9uYWwgYW5kIGlmIGl0IGlzIG5vdCBzcGVjaWZpZWRcbiAgLy8gdGhlIENMSSB3aWxsIHVzZSB0aGUgYXJ0aWZhY3QgSUQgYXMgdGhlIHN0YWNrIG5hbWUuIHdlICpjb3VsZCBoYXZlKlxuICAvLyBhbHdheXMgcHV0IHRoZSBzdGFjayBuYW1lIGhlcmUgYnV0IHdhbnRlZCB0byBtaW5pbWl6ZSB0aGUgcmlzayBhcm91bmRcbiAgLy8gY2hhbmdlcyB0byB0aGUgYXNzZW1ibHkgbWFuaWZlc3QuIHNvIHRoaXMgbWVhbnMgdGhhdCBhcyBsb25nIGFzIHN0YWNrXG4gIC8vIG5hbWUgYW5kIGFydGlmYWN0IElEIGFyZSB0aGUgc2FtZSwgdGhlIGNsb3VkIGFzc2VtYmx5IG1hbmlmZXN0IHdpbGwgbm90XG4gIC8vIGNoYW5nZS5cbiAgY29uc3Qgc3RhY2tOYW1lUHJvcGVydHkgPSBzdGFjay5zdGFja05hbWUgPT09IHN0YWNrLmFydGlmYWN0SWRcbiAgICA/IHsgfVxuICAgIDogeyBzdGFja05hbWU6IHN0YWNrLnN0YWNrTmFtZSB9O1xuXG4gIGNvbnN0IHByb3BlcnRpZXM6IGN4c2NoZW1hLkF3c0Nsb3VkRm9ybWF0aW9uU3RhY2tQcm9wZXJ0aWVzID0ge1xuICAgIHRlbXBsYXRlRmlsZTogc3RhY2sudGVtcGxhdGVGaWxlLFxuICAgIHRlcm1pbmF0aW9uUHJvdGVjdGlvbjogc3RhY2sudGVybWluYXRpb25Qcm90ZWN0aW9uLFxuICAgIHRhZ3M6IG5vbkVtcHR5RGljdChzdGFjay50YWdzLnRhZ1ZhbHVlcygpKSxcbiAgICAuLi5zdGFja1Byb3BzLFxuICAgIC4uLnN0YWNrTmFtZVByb3BlcnR5LFxuICB9O1xuXG4gIC8vIGFkZCBhbiBhcnRpZmFjdCB0aGF0IHJlcHJlc2VudHMgdGhpcyBzdGFja1xuICBzZXNzaW9uLmFzc2VtYmx5LmFkZEFydGlmYWN0KHN0YWNrLmFydGlmYWN0SWQsIHtcbiAgICB0eXBlOiBjeHNjaGVtYS5BcnRpZmFjdFR5cGUuQVdTX0NMT1VERk9STUFUSU9OX1NUQUNLLFxuICAgIGVudmlyb25tZW50OiBzdGFjay5lbnZpcm9ubWVudCxcbiAgICBwcm9wZXJ0aWVzLFxuICAgIGRlcGVuZGVuY2llczogZGVwcy5sZW5ndGggPiAwID8gZGVwcyA6IHVuZGVmaW5lZCxcbiAgICBtZXRhZGF0YTogT2JqZWN0LmtleXMobWV0YSkubGVuZ3RoID4gMCA/IG1ldGEgOiB1bmRlZmluZWQsXG4gIH0pO1xufVxuXG4vKipcbiAqIENvbGxlY3QgdGhlIG1ldGFkYXRhIGZyb20gYSBzdGFja1xuICovXG5mdW5jdGlvbiBjb2xsZWN0U3RhY2tNZXRhZGF0YShzdGFjazogU3RhY2spIHtcbiAgY29uc3Qgb3V0cHV0OiB7IFtpZDogc3RyaW5nXTogY3hzY2hlbWEuTWV0YWRhdGFFbnRyeVtdIH0gPSB7IH07XG5cbiAgdmlzaXQoc3RhY2spO1xuXG4gIHJldHVybiBvdXRwdXQ7XG5cbiAgZnVuY3Rpb24gdmlzaXQobm9kZTogSUNvbnN0cnVjdCkge1xuICAgIC8vIGJyZWFrIG9mZiBpZiB3ZSByZWFjaGVkIGEgbm9kZSB0aGF0IGlzIG5vdCBhIGNoaWxkIG9mIHRoaXMgc3RhY2tcbiAgICBjb25zdCBwYXJlbnQgPSBmaW5kUGFyZW50U3RhY2sobm9kZSk7XG4gICAgaWYgKHBhcmVudCAhPT0gc3RhY2spIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBpZiAobm9kZS5ub2RlLm1ldGFkYXRhLmxlbmd0aCA+IDApIHtcbiAgICAgIC8vIE1ha2UgdGhlIHBhdGggYWJzb2x1dGVcbiAgICAgIG91dHB1dFtDb25zdHJ1Y3ROb2RlLlBBVEhfU0VQICsgbm9kZS5ub2RlLnBhdGhdID0gbm9kZS5ub2RlLm1ldGFkYXRhLm1hcChtZCA9PiBzdGFjay5yZXNvbHZlKG1kKSBhcyBjeHNjaGVtYS5NZXRhZGF0YUVudHJ5KTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IGNoaWxkIG9mIG5vZGUubm9kZS5jaGlsZHJlbikge1xuICAgICAgdmlzaXQoY2hpbGQpO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGZpbmRQYXJlbnRTdGFjayhub2RlOiBJQ29uc3RydWN0KTogU3RhY2sgfCB1bmRlZmluZWQge1xuICAgIGlmIChub2RlIGluc3RhbmNlb2YgU3RhY2sgJiYgbm9kZS5uZXN0ZWRTdGFja1BhcmVudCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gbm9kZTtcbiAgICB9XG5cbiAgICBpZiAoIW5vZGUubm9kZS5zY29wZSkge1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICByZXR1cm4gZmluZFBhcmVudFN0YWNrKG5vZGUubm9kZS5zY29wZSk7XG4gIH1cbn1cblxuLyoqXG4gKiBIYXNoIGEgc3RyaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb250ZW50SGFzaChjb250ZW50OiBzdHJpbmcpIHtcbiAgcmV0dXJuIGNyeXB0by5jcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoY29udGVudCkuZGlnZXN0KCdoZXgnKTtcbn1cblxuLyoqXG4gKiBUaHJvdyBhbiBlcnJvciBtZXNzYWdlIGFib3V0IGJpbmRpbmcoKSBpZiB3ZSBkb24ndCBoYXZlIGEgdmFsdWUgZm9yIHguXG4gKlxuICogVGhpcyByZXBsYWNlcyB0aGUgISBhc3NlcnRpb25zIHdlIHdvdWxkIG5lZWQgZXZlcnl3aGVyZSBvdGhlcndpc2UuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnRCb3VuZDxBPih4OiBBIHwgdW5kZWZpbmVkKTogYXNzZXJ0cyB4IGlzIE5vbk51bGxhYmxlPEE+IHtcbiAgaWYgKHggPT09IG51bGwgJiYgeCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdZb3UgbXVzdCBjYWxsIGJpbmRTdGFjaygpIGZpcnN0Jyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gbm9uRW1wdHlEaWN0PEE+KHhzOiBSZWNvcmQ8c3RyaW5nLCBBPikge1xuICByZXR1cm4gT2JqZWN0LmtleXMoeHMpLmxlbmd0aCA+IDAgPyB4cyA6IHVuZGVmaW5lZDtcbn0iXX0=