UNPKG

16.1 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.VERSION_LOCKED = exports.trimFromStart = exports.calculateFunctionHash = void 0;
4const crypto = require("crypto");
5const core_1 = require("@aws-cdk/core");
6const cx_api_1 = require("@aws-cdk/cx-api");
7const function_1 = require("./function");
8function calculateFunctionHash(fn) {
9 const stack = core_1.Stack.of(fn);
10 const functionResource = fn.node.defaultChild;
11 // render the cloudformation resource from this function
12 const config = stack.resolve(functionResource._toCloudFormation());
13 // config is of the shape: { Resources: { LogicalId: { Type: 'Function', Properties: { ... } }}}
14 const resources = config.Resources;
15 const resourceKeys = Object.keys(resources);
16 if (resourceKeys.length !== 1) {
17 throw new Error(`Expected one rendered CloudFormation resource but found ${resourceKeys.length}`);
18 }
19 const logicalId = resourceKeys[0];
20 const properties = resources[logicalId].Properties;
21 let stringifiedConfig;
22 if (core_1.FeatureFlags.of(fn).isEnabled(cx_api_1.LAMBDA_RECOGNIZE_VERSION_PROPS)) {
23 const updatedProps = sortProperties(filterUsefulKeys(properties));
24 stringifiedConfig = JSON.stringify(updatedProps);
25 }
26 else {
27 const sorted = sortProperties(properties);
28 config.Resources[logicalId].Properties = sorted;
29 stringifiedConfig = JSON.stringify(config);
30 }
31 const hash = crypto.createHash('md5');
32 hash.update(stringifiedConfig);
33 return hash.digest('hex');
34}
35exports.calculateFunctionHash = calculateFunctionHash;
36function trimFromStart(s, maxLength) {
37 const desiredLength = Math.min(maxLength, s.length);
38 const newStart = s.length - desiredLength;
39 return s.substring(newStart);
40}
41exports.trimFromStart = trimFromStart;
42/*
43 * The list of properties found in CfnFunction (or AWS::Lambda::Function).
44 * They are classified as "locked" to a Function Version or not.
45 * When a property is locked, any change to that property will not take effect on previously created Versions.
46 * Instead, a new Version must be generated for the change to take effect.
47 * Similarly, if a property that's not locked to a Version is modified, a new Version
48 * must not be generated.
49 *
50 * Adding a new property to this list - If the property is part of the UpdateFunctionConfiguration
51 * API or UpdateFunctionCode API, then it must be classified as true, otherwise false.
52 * See https://docs.aws.amazon.com/lambda/latest/dg/API_UpdateFunctionConfiguration.html and
53 * https://docs.aws.amazon.com/lambda/latest/dg/API_UpdateFunctionConfiguration.html
54 */
55exports.VERSION_LOCKED = {
56 // locked to the version
57 Architectures: true,
58 Code: true,
59 DeadLetterConfig: true,
60 Description: true,
61 Environment: true,
62 EphemeralStorage: true,
63 FileSystemConfigs: true,
64 FunctionName: true,
65 Handler: true,
66 ImageConfig: true,
67 KmsKeyArn: true,
68 Layers: true,
69 MemorySize: true,
70 PackageType: true,
71 Role: true,
72 Runtime: true,
73 Timeout: true,
74 TracingConfig: true,
75 VpcConfig: true,
76 // not locked to the version
77 CodeSigningConfigArn: false,
78 ReservedConcurrentExecutions: false,
79 Tags: false,
80};
81function filterUsefulKeys(properties) {
82 const versionProps = { ...exports.VERSION_LOCKED, ...function_1.Function._VER_PROPS };
83 const unclassified = Object.entries(properties)
84 .filter(([k, v]) => v != null && !Object.keys(versionProps).includes(k))
85 .map(([k, _]) => k);
86 if (unclassified.length > 0) {
87 throw new Error(`The following properties are not recognized as version properties: [${unclassified}].`
88 + ' See the README of the aws-lambda module to learn more about this and to fix it.');
89 }
90 const notLocked = Object.entries(versionProps).filter(([_, v]) => !v).map(([k, _]) => k);
91 notLocked.forEach(p => delete properties[p]);
92 const ret = {};
93 Object.entries(properties).filter(([k, _]) => versionProps[k]).forEach(([k, v]) => ret[k] = v);
94 return ret;
95}
96function sortProperties(properties) {
97 const ret = {};
98 // We take all required properties in the order that they were historically,
99 // to make sure the hash we calculate is stable.
100 // There cannot be more required properties added in the future,
101 // as that would be a backwards-incompatible change.
102 const requiredProperties = ['Code', 'Handler', 'Role', 'Runtime'];
103 for (const requiredProperty of requiredProperties) {
104 ret[requiredProperty] = properties[requiredProperty];
105 }
106 // then, add all of the non-required properties,
107 // in the original order
108 for (const property of Object.keys(properties)) {
109 if (requiredProperties.indexOf(property) === -1) {
110 ret[property] = properties[property];
111 }
112 }
113 return ret;
114}
115//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuY3Rpb24taGFzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImZ1bmN0aW9uLWhhc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUNBQWlDO0FBQ2pDLHdDQUFpRTtBQUNqRSw0Q0FBaUU7QUFDakUseUNBQXdEO0FBRXhELFNBQWdCLHFCQUFxQixDQUFDLEVBQWtCO0lBQ3RELE1BQU0sS0FBSyxHQUFHLFlBQUssQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFFM0IsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFlBQTJCLENBQUM7SUFFN0Qsd0RBQXdEO0lBQ3hELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUUsZ0JBQXdCLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDO0lBQzVFLGdHQUFnRztJQUNoRyxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDO0lBQ25DLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDNUMsSUFBSSxZQUFZLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLDJEQUEyRCxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztLQUNuRztJQUNELE1BQU0sU0FBUyxHQUFHLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsQyxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsVUFBVSxDQUFDO0lBRW5ELElBQUksaUJBQWlCLENBQUM7SUFDdEIsSUFBSSxtQkFBWSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxTQUFTLENBQUMsdUNBQThCLENBQUMsRUFBRTtRQUNqRSxNQUFNLFlBQVksR0FBRyxjQUFjLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztRQUNsRSxpQkFBaUIsR0FBRyxJQUFJLENBQUMs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RCxPQUFPLEdBQUcsQ0FBQztBQUNiLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBjcnlwdG8gZnJvbSAnY3J5cHRvJztcbmltcG9ydCB7IENmblJlc291cmNlLCBGZWF0dXJlRmxhZ3MsIFN0YWNrIH0gZnJvbSAnQGF3cy1jZGsvY29yZSc7XG5pbXBvcnQgeyBMQU1CREFfUkVDT0dOSVpFX1ZFUlNJT05fUFJPUFMgfSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0IHsgRnVuY3Rpb24gYXMgTGFtYmRhRnVuY3Rpb24gfSBmcm9tICcuL2Z1bmN0aW9uJztcblxuZXhwb3J0IGZ1bmN0aW9uIGNhbGN1bGF0ZUZ1bmN0aW9uSGFzaChmbjogTGFtYmRhRnVuY3Rpb24pIHtcbiAgY29uc3Qgc3RhY2sgPSBTdGFjay5vZihmbik7XG5cbiAgY29uc3QgZnVuY3Rpb25SZXNvdXJjZSA9IGZuLm5vZGUuZGVmYXVsdENoaWxkIGFzIENmblJlc291cmNlO1xuXG4gIC8vIHJlbmRlciB0aGUgY2xvdWRmb3JtYXRpb24gcmVzb3VyY2UgZnJvbSB0aGlzIGZ1bmN0aW9uXG4gIGNvbnN0IGNvbmZpZyA9IHN0YWNrLnJlc29sdmUoKGZ1bmN0aW9uUmVzb3VyY2UgYXMgYW55KS5fdG9DbG91ZEZvcm1hdGlvbigpKTtcbiAgLy8gY29uZmlnIGlzIG9mIHRoZSBzaGFwZTogeyBSZXNvdXJjZXM6IHsgTG9naWNhbElkOiB7IFR5cGU6ICdGdW5jdGlvbicsIFByb3BlcnRpZXM6IHsgLi4uIH0gfX19XG4gIGNvbnN0IHJlc291cmNlcyA9IGNvbmZpZy5SZXNvdXJjZXM7XG4gIGNvbnN0IHJlc291cmNlS2V5cyA9IE9iamVjdC5rZXlzKHJlc291cmNlcyk7XG4gIGlmIChyZXNvdXJjZUtleXMubGVuZ3RoICE9PSAxKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBvbmUgcmVuZGVyZWQgQ2xvdWRGb3JtYXRpb24gcmVzb3VyY2UgYnV0IGZvdW5kICR7cmVzb3VyY2VLZXlzLmxlbmd0aH1gKTtcbiAgfVxuICBjb25zdCBsb2dpY2FsSWQgPSByZXNvdXJjZUtleXNbMF07XG4gIGNvbnN0IHByb3BlcnRpZXMgPSByZXNvdXJjZXNbbG9naWNhbElkXS5Qcm9wZXJ0aWVzO1xuXG4gIGxldCBzdHJpbmdpZmllZENvbmZpZztcbiAgaWYgKEZlYXR1cmVGbGFncy5vZihmbikuaXNFbmFibGVkKExBTUJEQV9SRUNPR05JWkVfVkVSU0lPTl9QUk9QUykpIHtcbiAgICBjb25zdCB1cGRhdGVkUHJvcHMgPSBzb3J0UHJvcGVydGllcyhmaWx0ZXJVc2VmdWxLZXlzKHByb3BlcnRpZXMpKTtcbiAgICBzdHJpbmdpZmllZENvbmZpZyA9IEpTT04uc3RyaW5naWZ5KHVwZGF0ZWRQcm9wcyk7XG4gIH0gZWxzZSB7XG4gICAgY29uc3Qgc29ydGVkID0gc29ydFByb3BlcnRpZXMocHJvcGVydGllcyk7XG4gICAgY29uZmlnLlJlc291cmNlc1tsb2dpY2FsSWRdLlByb3BlcnRpZXMgPSBzb3J0ZWQ7XG4gICAgc3RyaW5naWZpZWRDb25maWcgPSBKU09OLnN0cmluZ2lmeShjb25maWcpO1xuICB9XG5cbiAgY29uc3QgaGFzaCA9IGNyeXB0by5jcmVhdGVIYXNoKCdtZDUnKTtcbiAgaGFzaC51cGRhdGUoc3RyaW5naWZpZWRDb25maWcpO1xuICByZXR1cm4gaGFzaC5kaWdlc3QoJ2hleCcpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gdHJpbUZyb21TdGFydChzOiBzdHJpbmcsIG1heExlbmd0aDogbnVtYmVyKSB7XG4gIGNvbnN0IGRlc2lyZWRMZW5ndGggPSBNYXRoLm1pbihtYXhMZW5ndGgsIHMubGVuZ3RoKTtcbiAgY29uc3QgbmV3U3RhcnQgPSBzLmxlbmd0aCAtIGRlc2lyZWRMZW5ndGg7XG4gIHJldHVybiBzLnN1YnN0cmluZyhuZXdTdGFydCk7XG59XG5cbi8qXG4gKiBUaGUgbGlzdCBvZiBwcm9wZXJ0aWVzIGZvdW5kIGluIENmbkZ1bmN0aW9uIChvciBBV1M6OkxhbWJkYTo6RnVuY3Rpb24pLlxuICogVGhleSBhcmUgY2xhc3NpZmllZCBhcyBcImxvY2tlZFwiIHRvIGEgRnVuY3Rpb24gVmVyc2lvbiBvciBub3QuXG4gKiBXaGVuIGEgcHJvcGVydHkgaXMgbG9ja2VkLCBhbnkgY2hhbmdlIHRvIHRoYXQgcHJvcGVydHkgd2lsbCBub3QgdGFrZSBlZmZlY3Qgb24gcHJldmlvdXNseSBjcmVhdGVkIFZlcnNpb25zLlxuICogSW5zdGVhZCwgYSBuZXcgVmVyc2lvbiBtdXN0IGJlIGdlbmVyYXRlZCBmb3IgdGhlIGNoYW5nZSB0byB0YWtlIGVmZmVjdC5cbiAqIFNpbWlsYXJseSwgaWYgYSBwcm9wZXJ0eSB0aGF0J3Mgbm90IGxvY2tlZCB0byBhIFZlcnNpb24gaXMgbW9kaWZpZWQsIGEgbmV3IFZlcnNpb25cbiAqIG11c3Qgbm90IGJlIGdlbmVyYXRlZC5cbiAqXG4gKiBBZGRpbmcgYSBuZXcgcHJvcGVydHkgdG8gdGhpcyBsaXN0IC0gSWYgdGhlIHByb3BlcnR5IGlzIHBhcnQgb2YgdGhlIFVwZGF0ZUZ1bmN0aW9uQ29uZmlndXJhdGlvblxuICogQVBJIG9yIFVwZGF0ZUZ1bmN0aW9uQ29kZSBBUEksIHRoZW4gaXQgbXVzdCBiZSBjbGFzc2lmaWVkIGFzIHRydWUsIG90aGVyd2lzZSBmYWxzZS5cbiAqIFNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vbGFtYmRhL2xhdGVzdC9kZy9BUElfVXBkYXRlRnVuY3Rpb25Db25maWd1cmF0aW9uLmh0bWwgYW5kXG4gKiBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vbGFtYmRhL2xhdGVzdC9kZy9BUElfVXBkYXRlRnVuY3Rpb25Db25maWd1cmF0aW9uLmh0bWxcbiAqL1xuZXhwb3J0IGNvbnN0IFZFUlNJT05fTE9DS0VEOiB7IFtrZXk6IHN0cmluZ106IGJvb2xlYW4gfSA9IHtcbiAgLy8gbG9ja2VkIHRvIHRoZSB2ZXJzaW9uXG4gIEFyY2hpdGVjdHVyZXM6IHRydWUsXG4gIENvZGU6IHRydWUsXG4gIERlYWRMZXR0ZXJDb25maWc6IHRydWUsXG4gIERlc2NyaXB0aW9uOiB0cnVlLFxuICBFbnZpcm9ubWVudDogdHJ1ZSxcbiAgRXBoZW1lcmFsU3RvcmFnZTogdHJ1ZSxcbiAgRmlsZVN5c3RlbUNvbmZpZ3M6IHRydWUsXG4gIEZ1bmN0aW9uTmFtZTogdHJ1ZSxcbiAgSGFuZGxlcjogdHJ1ZSxcbiAgSW1hZ2VDb25maWc6IHRydWUsXG4gIEttc0tleUFybjogdHJ1ZSxcbiAgTGF5ZXJzOiB0cnVlLFxuICBNZW1vcnlTaXplOiB0cnVlLFxuICBQYWNrYWdlVHlwZTogdHJ1ZSxcbiAgUm9sZTogdHJ1ZSxcbiAgUnVudGltZTogdHJ1ZSxcbiAgVGltZW91dDogdHJ1ZSxcbiAgVHJhY2luZ0NvbmZpZzogdHJ1ZSxcbiAgVnBjQ29uZmlnOiB0cnVlLFxuXG4gIC8vIG5vdCBsb2NrZWQgdG8gdGhlIHZlcnNpb25cbiAgQ29kZVNpZ25pbmdDb25maWdBcm46IGZhbHNlLFxuICBSZXNlcnZlZENvbmN1cnJlbnRFeGVjdXRpb25zOiBmYWxzZSxcbiAgVGFnczogZmFsc2UsXG59O1xuXG5mdW5jdGlvbiBmaWx0ZXJVc2VmdWxLZXlzKHByb3BlcnRpZXM6IGFueSkge1xuICBjb25zdCB2ZXJzaW9uUHJvcHMgPSB7IC4uLlZFUlNJT05fTE9DS0VELCAuLi5MYW1iZGFGdW5jdGlvbi5fVkVSX1BST1BTIH07XG4gIGNvbnN0IHVuY2xhc3NpZmllZCA9IE9iamVjdC5lbnRyaWVzKHByb3BlcnRpZXMpXG4gICAgLmZpbHRlcigoW2ssIHZdKSA9PiB2ICE9IG51bGwgJiYgIU9iamVjdC5rZXlzKHZlcnNpb25Qcm9wcykuaW5jbHVkZXMoaykpXG4gICAgLm1hcCgoW2ssIF9dKSA9PiBrKTtcbiAgaWYgKHVuY2xhc3NpZmllZC5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBUaGUgZm9sbG93aW5nIHByb3BlcnRpZXMgYXJlIG5vdCByZWNvZ25pemVkIGFzIHZlcnNpb24gcHJvcGVydGllczogWyR7dW5jbGFzc2lmaWVkfV0uYFxuICAgICAgKyAnIFNlZSB0aGUgUkVBRE1FIG9mIHRoZSBhd3MtbGFtYmRhIG1vZHVsZSB0byBsZWFybiBtb3JlIGFib3V0IHRoaXMgYW5kIHRvIGZpeCBpdC4nKTtcbiAgfVxuICBjb25zdCBub3RMb2NrZWQgPSBPYmplY3QuZW50cmllcyh2ZXJzaW9uUHJvcHMpLmZpbHRlcigoW18sIHZdKSA9PiAhdikubWFwKChbaywgX10pID0+IGspO1xuICBub3RMb2NrZWQuZm9yRWFjaChwID0+IGRlbGV0ZSBwcm9wZXJ0aWVzW3BdKTtcblxuICBjb25zdCByZXQ6IHsgW2tleTogc3RyaW5nXTogYW55IH0gPSB7fTtcbiAgT2JqZWN0LmVudHJpZXMocHJvcGVydGllcykuZmlsdGVyKChbaywgX10pID0+IHZlcnNpb25Qcm9wc1trXSkuZm9yRWFjaCgoW2ssIHZdKSA9PiByZXRba10gPSB2KTtcbiAgcmV0dXJuIHJldDtcbn1cblxuZnVuY3Rpb24gc29ydFByb3BlcnRpZXMocHJvcGVydGllczogYW55KSB7XG4gIGNvbnN0IHJldDogYW55ID0ge307XG4gIC8vIFdlIHRha2UgYWxsIHJlcXVpcmVkIHByb3BlcnRpZXMgaW4gdGhlIG9yZGVyIHRoYXQgdGhleSB3ZXJlIGhpc3RvcmljYWxseSxcbiAgLy8gdG8gbWFrZSBzdXJlIHRoZSBoYXNoIHdlIGNhbGN1bGF0ZSBpcyBzdGFibGUuXG4gIC8vIFRoZXJlIGNhbm5vdCBiZSBtb3JlIHJlcXVpcmVkIHByb3BlcnRpZXMgYWRkZWQgaW4gdGhlIGZ1dHVyZSxcbiAgLy8gYXMgdGhhdCB3b3VsZCBiZSBhIGJhY2t3YXJkcy1pbmNvbXBhdGlibGUgY2hhbmdlLlxuICBjb25zdCByZXF1aXJlZFByb3BlcnRpZXMgPSBbJ0NvZGUnLCAnSGFuZGxlcicsICdSb2xlJywgJ1J1bnRpbWUnXTtcbiAgZm9yIChjb25zdCByZXF1aXJlZFByb3BlcnR5IG9mIHJlcXVpcmVkUHJvcGVydGllcykge1xuICAgIHJldFtyZXF1aXJlZFByb3BlcnR5XSA9IHByb3BlcnRpZXNbcmVxdWlyZWRQcm9wZXJ0eV07XG4gIH1cbiAgLy8gdGhlbiwgYWRkIGFsbCBvZiB0aGUgbm9uLXJlcXVpcmVkIHByb3BlcnRpZXMsXG4gIC8vIGluIHRoZSBvcmlnaW5hbCBvcmRlclxuICBmb3IgKGNvbnN0IHByb3BlcnR5IG9mIE9iamVjdC5rZXlzKHByb3BlcnRpZXMpKSB7XG4gICAgaWYgKHJlcXVpcmVkUHJvcGVydGllcy5pbmRleE9mKHByb3BlcnR5KSA9PT0gLTEpIHtcbiAgICAgIHJldFtwcm9wZXJ0eV0gPSBwcm9wZXJ0aWVzW3Byb3BlcnR5XTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIHJldDtcbn1cbiJdfQ==
\No newline at end of file