function-hash.js
1 "use strict"; 2 Object.defineProperty(exports, "__esModule", { value: true }); 3 exports.VERSION_LOCKED = exports.trimFromStart = exports.calculateFunctionHash = void 0; 4 const crypto = require("crypto"); 5 const core_1 = require("@aws-cdk/core"); 6 const cx_api_1 = require("@aws-cdk/cx-api"); 7 const function_1 = require("./function"); 8 function 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 } 35 exports.calculateFunctionHash = calculateFunctionHash; 36 function trimFromStart(s, maxLength) { 37 const desiredLength = Math.min(maxLength, s.length); 38 const newStart = s.length - desiredLength; 39 return s.substring(newStart); 40 } 41 exports.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 */ 55 exports.VERSION_LOCKED = { 56 // locked to the version 57 Architectures: true, 58 Code: true, 59 DeadLetterConfig: true, 60 Description: true, 61 Environment: true, 62 FileSystemConfigs: true, 63 FunctionName: true, 64 Handler: true, 65 ImageConfig: true, 66 KmsKeyArn: true, 67 Layers: true, 68 MemorySize: true, 69 PackageType: true, 70 Role: true, 71 Runtime: true, 72 Timeout: true, 73 TracingConfig: true, 74 VpcConfig: true, 75 // not locked to the version 76 CodeSigningConfigArn: false, 77 ReservedConcurrentExecutions: false, 78 Tags: false, 79 }; 80 function filterUsefulKeys(properties) { 81 const versionProps = { ...exports.VERSION_LOCKED, ...function_1.Function._VER_PROPS }; 82 const unclassified = Object.entries(properties) 83 .filter(([k, v]) => v != null && !Object.keys(versionProps).includes(k)) 84 .map(([k, _]) => k); 85 if (unclassified.length > 0) { 86 throw new Error(`The following properties are not recognized as version properties: [${unclassified}].` 87 + ' See the README of the aws-lambda module to learn more about this and to fix it.'); 88 } 89 const notLocked = Object.entries(versionProps).filter(([_, v]) => !v).map(([k, _]) => k); 90 notLocked.forEach(p => delete properties[p]); 91 const ret = {}; 92 Object.entries(properties).filter(([k, _]) => versionProps[k]).forEach(([k, v]) => ret[k] = v); 93 return ret; 94 } 95 function sortProperties(properties) { 96 const ret = {}; 97 // We take all required properties in the order that they were historically, 98 // to make sure the hash we calculate is stable. 99 // There cannot be more required properties added in the future, 100 // as that would be a backwards-incompatible change. 101 const requiredProperties = ['Code', 'Handler', 'Role', 'Runtime']; 102 for (const requiredProperty of requiredProperties) { 103 ret[requiredProperty] = properties[requiredProperty]; 104 } 105 // then, add all of the non-required properties, 106 // in the original order 107 for (const property of Object.keys(properties)) { 108 if (requiredProperties.indexOf(property) === -1) { 109 ret[property] = properties[property]; 110 } 111 } 112 return ret; 113 } 114 //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnVuY3Rpb24taGFzaC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImZ1bmN0aW9uLWhhc2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsaUNBQWlDO0FBQ2pDLHdDQUFpRTtBQUNqRSw0Q0FBaUU7QUFDakUseUNBQXdEO0FBRXhELFNBQWdCLHFCQUFxQixDQUFDLEVBQWtCO0lBQ3RELE1BQU0sS0FBSyxHQUFHLFlBQUssQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFFM0IsTUFBTSxnQkFBZ0IsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLFlBQTJCLENBQUM7SUFFN0Qsd0RBQXdEO0lBQ3hELE1BQU0sTUFBTSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUUsZ0JBQXdCLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDO0lBQzVFLGdHQUFnRztJQUNoRyxNQUFNLFNBQVMsR0FBRyxNQUFNLENBQUMsU0FBUyxDQUFDO0lBQ25DLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDNUMsSUFBSSxZQUFZLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRTtRQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLDJEQUEyRCxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsQ0FBQztLQUNuRztJQUNELE1BQU0sU0FBUyxHQUFHLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsQyxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsU0FBUyxDQUFDLENBQUMsVUFBVSxDQUFDO0lBRW5ELElBQUksaUJBQWlCLENBQUM7SUFDdEIsSUFBSSxtQkFBWSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxTQUFTLENBQUMsdUNBQThCLENBQUMsRUFBRTtRQUNqRSxNQUFNLFlBQVksR0FBRyxjQUFjLENBQUMsZ0JBQWdCLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQztRQUNsRSxpQkFBaUIsR0FBRyxJQUFJLENBQUMs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QUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgeyBDZm5SZXNvdXJjZSwgRmVhdHVyZUZsYWdzLCBTdGFjayB9IGZyb20gJ0Bhd3MtY2RrL2NvcmUnO1xuaW1wb3J0IHsgTEFNQkRBX1JFQ09HTklaRV9WRVJTSU9OX1BST1BTIH0gZnJvbSAnQGF3cy1jZGsvY3gtYXBpJztcbmltcG9ydCB7IEZ1bmN0aW9uIGFzIExhbWJkYUZ1bmN0aW9uIH0gZnJvbSAnLi9mdW5jdGlvbic7XG5cbmV4cG9ydCBmdW5jdGlvbiBjYWxjdWxhdGVGdW5jdGlvbkhhc2goZm46IExhbWJkYUZ1bmN0aW9uKSB7XG4gIGNvbnN0IHN0YWNrID0gU3RhY2sub2YoZm4pO1xuXG4gIGNvbnN0IGZ1bmN0aW9uUmVzb3VyY2UgPSBmbi5ub2RlLmRlZmF1bHRDaGlsZCBhcyBDZm5SZXNvdXJjZTtcblxuICAvLyByZW5kZXIgdGhlIGNsb3VkZm9ybWF0aW9uIHJlc291cmNlIGZyb20gdGhpcyBmdW5jdGlvblxuICBjb25zdCBjb25maWcgPSBzdGFjay5yZXNvbHZlKChmdW5jdGlvblJlc291cmNlIGFzIGFueSkuX3RvQ2xvdWRGb3JtYXRpb24oKSk7XG4gIC8vIGNvbmZpZyBpcyBvZiB0aGUgc2hhcGU6IHsgUmVzb3VyY2VzOiB7IExvZ2ljYWxJZDogeyBUeXBlOiAnRnVuY3Rpb24nLCBQcm9wZXJ0aWVzOiB7IC4uLiB9IH19fVxuICBjb25zdCByZXNvdXJjZXMgPSBjb25maWcuUmVzb3VyY2VzO1xuICBjb25zdCByZXNvdXJjZUtleXMgPSBPYmplY3Qua2V5cyhyZXNvdXJjZXMpO1xuICBpZiAocmVzb3VyY2VLZXlzLmxlbmd0aCAhPT0gMSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgRXhwZWN0ZWQgb25lIHJlbmRlcmVkIENsb3VkRm9ybWF0aW9uIHJlc291cmNlIGJ1dCBmb3VuZCAke3Jlc291cmNlS2V5cy5sZW5ndGh9YCk7XG4gIH1cbiAgY29uc3QgbG9naWNhbElkID0gcmVzb3VyY2VLZXlzWzBdO1xuICBjb25zdCBwcm9wZXJ0aWVzID0gcmVzb3VyY2VzW2xvZ2ljYWxJZF0uUHJvcGVydGllcztcblxuICBsZXQgc3RyaW5naWZpZWRDb25maWc7XG4gIGlmIChGZWF0dXJlRmxhZ3Mub2YoZm4pLmlzRW5hYmxlZChMQU1CREFfUkVDT0dOSVpFX1ZFUlNJT05fUFJPUFMpKSB7XG4gICAgY29uc3QgdXBkYXRlZFByb3BzID0gc29ydFByb3BlcnRpZXMoZmlsdGVyVXNlZnVsS2V5cyhwcm9wZXJ0aWVzKSk7XG4gICAgc3RyaW5naWZpZWRDb25maWcgPSBKU09OLnN0cmluZ2lmeSh1cGRhdGVkUHJvcHMpO1xuICB9IGVsc2Uge1xuICAgIGNvbnN0IHNvcnRlZCA9IHNvcnRQcm9wZXJ0aWVzKHByb3BlcnRpZXMpO1xuICAgIGNvbmZpZy5SZXNvdXJjZXNbbG9naWNhbElkXS5Qcm9wZXJ0aWVzID0gc29ydGVkO1xuICAgIHN0cmluZ2lmaWVkQ29uZmlnID0gSlNPTi5zdHJpbmdpZnkoY29uZmlnKTtcbiAgfVxuXG4gIGNvbnN0IGhhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaCgnbWQ1Jyk7XG4gIGhhc2gudXBkYXRlKHN0cmluZ2lmaWVkQ29uZmlnKTtcbiAgcmV0dXJuIGhhc2guZGlnZXN0KCdoZXgnKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHRyaW1Gcm9tU3RhcnQoczogc3RyaW5nLCBtYXhMZW5ndGg6IG51bWJlcikge1xuICBjb25zdCBkZXNpcmVkTGVuZ3RoID0gTWF0aC5taW4obWF4TGVuZ3RoLCBzLmxlbmd0aCk7XG4gIGNvbnN0IG5ld1N0YXJ0ID0gcy5sZW5ndGggLSBkZXNpcmVkTGVuZ3RoO1xuICByZXR1cm4gcy5zdWJzdHJpbmcobmV3U3RhcnQpO1xufVxuXG4vKlxuICogVGhlIGxpc3Qgb2YgcHJvcGVydGllcyBmb3VuZCBpbiBDZm5GdW5jdGlvbiAob3IgQVdTOjpMYW1iZGE6OkZ1bmN0aW9uKS5cbiAqIFRoZXkgYXJlIGNsYXNzaWZpZWQgYXMgXCJsb2NrZWRcIiB0byBhIEZ1bmN0aW9uIFZlcnNpb24gb3Igbm90LlxuICogV2hlbiBhIHByb3BlcnR5IGlzIGxvY2tlZCwgYW55IGNoYW5nZSB0byB0aGF0IHByb3BlcnR5IHdpbGwgbm90IHRha2UgZWZmZWN0IG9uIHByZXZpb3VzbHkgY3JlYXRlZCBWZXJzaW9ucy5cbiAqIEluc3RlYWQsIGEgbmV3IFZlcnNpb24gbXVzdCBiZSBnZW5lcmF0ZWQgZm9yIHRoZSBjaGFuZ2UgdG8gdGFrZSBlZmZlY3QuXG4gKiBTaW1pbGFybHksIGlmIGEgcHJvcGVydHkgdGhhdCdzIG5vdCBsb2NrZWQgdG8gYSBWZXJzaW9uIGlzIG1vZGlmaWVkLCBhIG5ldyBWZXJzaW9uXG4gKiBtdXN0IG5vdCBiZSBnZW5lcmF0ZWQuXG4gKlxuICogQWRkaW5nIGEgbmV3IHByb3BlcnR5IHRvIHRoaXMgbGlzdCAtIElmIHRoZSBwcm9wZXJ0eSBpcyBwYXJ0IG9mIHRoZSBVcGRhdGVGdW5jdGlvbkNvbmZpZ3VyYXRpb25cbiAqIEFQSSBvciBVcGRhdGVGdW5jdGlvbkNvZGUgQVBJLCB0aGVuIGl0IG11c3QgYmUgY2xhc3NpZmllZCBhcyB0cnVlLCBvdGhlcndpc2UgZmFsc2UuXG4gKiBTZWUgaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2xhbWJkYS9sYXRlc3QvZGcvQVBJX1VwZGF0ZUZ1bmN0aW9uQ29uZmlndXJhdGlvbi5odG1sIGFuZFxuICogaHR0cHM6Ly9kb2NzLmF3cy5hbWF6b24uY29tL2xhbWJkYS9sYXRlc3QvZGcvQVBJX1VwZGF0ZUZ1bmN0aW9uQ29uZmlndXJhdGlvbi5odG1sXG4gKi9cbmV4cG9ydCBjb25zdCBWRVJTSU9OX0xPQ0tFRDogeyBba2V5OiBzdHJpbmddOiBib29sZWFuIH0gPSB7XG4gIC8vIGxvY2tlZCB0byB0aGUgdmVyc2lvblxuICBBcmNoaXRlY3R1cmVzOiB0cnVlLFxuICBDb2RlOiB0cnVlLFxuICBEZWFkTGV0dGVyQ29uZmlnOiB0cnVlLFxuICBEZXNjcmlwdGlvbjogdHJ1ZSxcbiAgRW52aXJvbm1lbnQ6IHRydWUsXG4gIEZpbGVTeXN0ZW1Db25maWdzOiB0cnVlLFxuICBGdW5jdGlvbk5hbWU6IHRydWUsXG4gIEhhbmRsZXI6IHRydWUsXG4gIEltYWdlQ29uZmlnOiB0cnVlLFxuICBLbXNLZXlBcm46IHRydWUsXG4gIExheWVyczogdHJ1ZSxcbiAgTWVtb3J5U2l6ZTogdHJ1ZSxcbiAgUGFja2FnZVR5cGU6IHRydWUsXG4gIFJvbGU6IHRydWUsXG4gIFJ1bnRpbWU6IHRydWUsXG4gIFRpbWVvdXQ6IHRydWUsXG4gIFRyYWNpbmdDb25maWc6IHRydWUsXG4gIFZwY0NvbmZpZzogdHJ1ZSxcblxuICAvLyBub3QgbG9ja2VkIHRvIHRoZSB2ZXJzaW9uXG4gIENvZGVTaWduaW5nQ29uZmlnQXJuOiBmYWxzZSxcbiAgUmVzZXJ2ZWRDb25jdXJyZW50RXhlY3V0aW9uczogZmFsc2UsXG4gIFRhZ3M6IGZhbHNlLFxufTtcblxuZnVuY3Rpb24gZmlsdGVyVXNlZnVsS2V5cyhwcm9wZXJ0aWVzOiBhbnkpIHtcbiAgY29uc3QgdmVyc2lvblByb3BzID0geyAuLi5WRVJTSU9OX0xPQ0tFRCwgLi4uTGFtYmRhRnVuY3Rpb24uX1ZFUl9QUk9QUyB9O1xuICBjb25zdCB1bmNsYXNzaWZpZWQgPSBPYmplY3QuZW50cmllcyhwcm9wZXJ0aWVzKVxuICAgIC5maWx0ZXIoKFtrLCB2XSkgPT4gdiAhPSBudWxsICYmICFPYmplY3Qua2V5cyh2ZXJzaW9uUHJvcHMpLmluY2x1ZGVzKGspKVxuICAgIC5tYXAoKFtrLCBfXSkgPT4gayk7XG4gIGlmICh1bmNsYXNzaWZpZWQubGVuZ3RoID4gMCkge1xuICAgIHRocm93IG5ldyBFcnJvcihgVGhlIGZvbGxvd2luZyBwcm9wZXJ0aWVzIGFyZSBub3QgcmVjb2duaXplZCBhcyB2ZXJzaW9uIHByb3BlcnRpZXM6IFske3VuY2xhc3NpZmllZH1dLmBcbiAgICAgICsgJyBTZWUgdGhlIFJFQURNRSBvZiB0aGUgYXdzLWxhbWJkYSBtb2R1bGUgdG8gbGVhcm4gbW9yZSBhYm91dCB0aGlzIGFuZCB0byBmaXggaXQuJyk7XG4gIH1cbiAgY29uc3Qgbm90TG9ja2VkID0gT2JqZWN0LmVudHJpZXModmVyc2lvblByb3BzKS5maWx0ZXIoKFtfLCB2XSkgPT4gIXYpLm1hcCgoW2ssIF9dKSA9PiBrKTtcbiAgbm90TG9ja2VkLmZvckVhY2gocCA9PiBkZWxldGUgcHJvcGVydGllc1twXSk7XG5cbiAgY29uc3QgcmV0OiB7IFtrZXk6IHN0cmluZ106IGFueSB9ID0ge307XG4gIE9iamVjdC5lbnRyaWVzKHByb3BlcnRpZXMpLmZpbHRlcigoW2ssIF9dKSA9PiB2ZXJzaW9uUHJvcHNba10pLmZvckVhY2goKFtrLCB2XSkgPT4gcmV0W2tdID0gdik7XG4gIHJldHVybiByZXQ7XG59XG5cbmZ1bmN0aW9uIHNvcnRQcm9wZXJ0aWVzKHByb3BlcnRpZXM6IGFueSkge1xuICBjb25zdCByZXQ6IGFueSA9IHt9O1xuICAvLyBXZSB0YWtlIGFsbCByZXF1aXJlZCBwcm9wZXJ0aWVzIGluIHRoZSBvcmRlciB0aGF0IHRoZXkgd2VyZSBoaXN0b3JpY2FsbHksXG4gIC8vIHRvIG1ha2Ugc3VyZSB0aGUgaGFzaCB3ZSBjYWxjdWxhdGUgaXMgc3RhYmxlLlxuICAvLyBUaGVyZSBjYW5ub3QgYmUgbW9yZSByZXF1aXJlZCBwcm9wZXJ0aWVzIGFkZGVkIGluIHRoZSBmdXR1cmUsXG4gIC8vIGFzIHRoYXQgd291bGQgYmUgYSBiYWNrd2FyZHMtaW5jb21wYXRpYmxlIGNoYW5nZS5cbiAgY29uc3QgcmVxdWlyZWRQcm9wZXJ0aWVzID0gWydDb2RlJywgJ0hhbmRsZXInLCAnUm9sZScsICdSdW50aW1lJ107XG4gIGZvciAoY29uc3QgcmVxdWlyZWRQcm9wZXJ0eSBvZiByZXF1aXJlZFByb3BlcnRpZXMpIHtcbiAgICByZXRbcmVxdWlyZWRQcm9wZXJ0eV0gPSBwcm9wZXJ0aWVzW3JlcXVpcmVkUHJvcGVydHldO1xuICB9XG4gIC8vIHRoZW4sIGFkZCBhbGwgb2YgdGhlIG5vbi1yZXF1aXJlZCBwcm9wZXJ0aWVzLFxuICAvLyBpbiB0aGUgb3JpZ2luYWwgb3JkZXJcbiAgZm9yIChjb25zdCBwcm9wZXJ0eSBvZiBPYmplY3Qua2V5cyhwcm9wZXJ0aWVzKSkge1xuICAgIGlmIChyZXF1aXJlZFByb3BlcnRpZXMuaW5kZXhPZihwcm9wZXJ0eSkgPT09IC0xKSB7XG4gICAgICByZXRbcHJvcGVydHldID0gcHJvcGVydGllc1twcm9wZXJ0eV07XG4gICAgfVxuICB9XG4gIHJldHVybiByZXQ7XG59XG4iXX0=