UNPKG

6.03 kBJavaScriptView Raw
1'use strict';
2
3function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
4
5const fs = require('fs-extra');
6const path = require('path');
7const util = require('util');
8const utils = require('kes').utils;
9const yauzl = require('yauzl');
10
11const { Lambda } = require('kes');
12
13/**
14 * A sub-class of the Kes Lambda class that changes
15 * how kes handles Lambda function compression and
16 * upload to S3.
17 *
18 * This sub-class adds cumulus-message-adapter to
19 * lambdas defined in a Kes configuration file.
20 */
21class UpdatedLambda extends Lambda {
22 /**
23 * Override the main constructor to allow
24 * passing the config object to the instance
25 * of the class
26 *
27 * @param {Object} config - Kes config object
28 */
29 constructor(config) {
30 super(config);
31 this.config = config;
32 }
33
34 /**
35 * Executes buildS3Path for all lambdas in a lambda configuration object
36 *
37 * Utilizes buildS3Path to populate bucket/hash values
38 * in the config object for a template that runs following a nested template
39 * that has already run the superclass 'process' method.
40 *
41 * @param {string} configKey - the configuration key with a lambda
42 * configuration object to be modified
43 * @returns {void} returns nothing
44 */
45 buildAllLambdaConfiguration(configKey) {
46 if (this.config[configKey]) {
47 let lambdas = this.config[configKey];
48 // if the lambdas is not an array but a object, convert it to a list
49 if (!Array.isArray(this.config[configKey])) {
50 lambdas = Object.keys(this.config[configKey]).map(name => {
51 const lambda = this.config[configKey][name];
52 lambda.name = name;
53 return lambda;
54 });
55 }
56 lambdas.forEach(lambda => this.buildS3Path(lambda));
57 }
58 }
59
60 /**
61 * Method adds hash value from each config.lambda to each
62 * defined workflow lambda in config.workflowLambdas
63 *
64 * @returns {void} returns nothing
65 */
66 addWorkflowLambdaHashes() {
67 Object.keys(this.config.lambdas).forEach(key => {
68 if (key in this.config.workflowLambdas && 'hash' in this.config.lambdas[key]) {
69 this.config.workflowLambdas[key].hash = this.config.lambdas[key].hash;
70 }
71 });
72 }
73
74 /**
75 * Copies the source code of a given lambda function, zips it, calculates
76 * the hash of the source code and updates the lambda object with
77 * the hash, local and remote locations of the code.
78 *
79 * @param {Object} lambda - the lambda object
80 * @returns {Promise} returns the updated lambda object
81 */
82 zipLambda(lambda) {
83 var _this = this;
84
85 return _asyncToGenerator(function* () {
86 // skip if the file with the same hash is zipped
87 // and is a valid zip file
88 if (yield fs.pathExists(lambda.local)) {
89 try {
90 yield util.promisify(yauzl.open)(lambda.local); // Verify yauzl can open the .zip file
91 return Promise.resolve(lambda);
92 } catch (e) {
93 console.log(`${lambda.local} is invalid and will be rebuilt`);
94 }
95 }
96
97 let msg = `Zipping ${lambda.local}`;
98 const fileList = [lambda.source];
99 if (lambda.useMessageAdapter) {
100 const kesFolder = path.join(_this.config.kesFolder, 'build', 'adapter');
101 fileList.push(kesFolder);
102 msg += ' and injecting message adapter';
103 }
104
105 console.log(`${msg} for ${lambda.name}`);
106
107 try {
108 yield utils.zip(lambda.local, fileList);
109 } catch (e) {
110 console.log(`Error zipping ${e}`);
111 throw e;
112 }
113
114 return lambda;
115 })();
116 }
117
118 getLambdaVersionFromPackageFile(sourceDir) {
119 let packageJson = '{}';
120 const JsonFilePath = `${sourceDir}/../package.json`;
121
122 try {
123 if (fs.existsSync(JsonFilePath)) {
124 packageJson = fs.readFileSync(`${JsonFilePath}`);
125 }
126 } catch (e) {
127 console.log(`Error reading package.json from ${JsonFilePath}`);
128 throw e;
129 }
130 const packageData = JSON.parse(packageJson);
131
132 if (!packageData || !packageData.version) {
133 return null;
134 }
135 return packageData.version;
136 }
137
138 /**
139 * Overrides the default method to allow returning
140 * the lambda function after s3 paths were built
141 *
142 * If a s3Source is used, only add remote and bucket values
143 *
144 * If a s3Source is used and a uniqueIdentifier is specified
145 * add that value in place of a calculated hash
146 *
147 * @param {Object} lambdaArg - the Lambda object
148 * @returns {Object} the updated lambda object
149 */
150 buildS3Path(lambdaArg) {
151 const lambda = super.buildS3Path(lambdaArg);
152
153 if (lambda.s3Source && lambda.s3Source.uniqueIdentifier) {
154 const uniqueIdentifier = lambda.s3Source.uniqueIdentifier;
155 if (!uniqueIdentifier.match(/^[a-z0-9]+$/)) {
156 throw new Error(`Invalid uniqueIdentifier ${uniqueIdentifier} provided for lambda`);
157 }
158 lambda.hash = uniqueIdentifier;
159 lambda.humanReadableIdentifier = uniqueIdentifier;
160 } else {
161 const lambdaVersion = this.getLambdaVersionFromPackageFile(lambda.source);
162 lambda.humanReadableIdentifier = lambdaVersion || lambda.hash;
163 }
164
165 // adding the hash of the message adapter zip file as part of lambda zip file
166 if (lambda.useMessageAdapter && UpdatedLambda.messageAdapterZipFileHash) {
167 lambda.local = path.join(path.dirname(lambda.local), `${UpdatedLambda.messageAdapterZipFileHash}-${path.basename(lambda.local)}`);
168 lambda.remote = path.join(path.dirname(lambda.remote), `${UpdatedLambda.messageAdapterZipFileHash}-${path.basename(lambda.remote)}`);
169 }
170
171 return lambda;
172 }
173}
174
175module.exports = UpdatedLambda;
176
177UpdatedLambda.messageAdapterZipFileHash = undefined;
\No newline at end of file