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=