elasticio-sailor-nodejs 2.7.0-dev2 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.nsprc +6 -0
- package/CHANGELOG.md +144 -0
- package/lib/amqp.js +272 -80
- package/lib/cipher.js +0 -82
- package/lib/component_reader.js +14 -4
- package/lib/encryptor.js +107 -14
- package/lib/executor.js +1 -1
- package/lib/ipc.js +13 -0
- package/lib/sailor.js +149 -121
- package/lib/service.js +1 -2
- package/lib/settings.js +72 -53
- package/package.json +13 -10
- package/run.js +60 -21
- package/runService.js +5 -1
- package/.travis.yml +0 -8
- package/Procfile +0 -2
- package/createQueues.js +0 -148
- package/gulpfile.js +0 -31
package/lib/cipher.js
CHANGED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const crypto = require('crypto');
|
|
3
|
-
const debug = require('debug')('sailor:cipher');
|
|
4
|
-
const { PassThrough } = require('stream');
|
|
5
|
-
|
|
6
|
-
const ALGORYTHM = 'aes-256-cbc';
|
|
7
|
-
|
|
8
|
-
exports.id = 1;
|
|
9
|
-
exports.encrypt = encryptIV;
|
|
10
|
-
exports.encryptStream = createCypher;
|
|
11
|
-
exports.decrypt = decryptIV;
|
|
12
|
-
exports.decryptStream = createDecipher;
|
|
13
|
-
|
|
14
|
-
function createCypher() {
|
|
15
|
-
const PASSWORD = process.env.ELASTICIO_MESSAGE_CRYPTO_PASSWORD;
|
|
16
|
-
const VECTOR = process.env.ELASTICIO_MESSAGE_CRYPTO_IV;
|
|
17
|
-
|
|
18
|
-
if (!PASSWORD) {
|
|
19
|
-
//mimic cypher
|
|
20
|
-
return new class extends PassThrough {
|
|
21
|
-
update(data) {
|
|
22
|
-
return data;
|
|
23
|
-
}
|
|
24
|
-
final() {
|
|
25
|
-
return '';
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!VECTOR) {
|
|
31
|
-
throw new Error('process.env.ELASTICIO_MESSAGE_CRYPTO_IV is not set');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const encodeKey = crypto.createHash('sha256').update(PASSWORD, 'utf-8').digest();
|
|
35
|
-
return crypto.createCipheriv(ALGORYTHM, encodeKey, VECTOR);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function createDecipher() {
|
|
39
|
-
const PASSWORD = process.env.ELASTICIO_MESSAGE_CRYPTO_PASSWORD;
|
|
40
|
-
const VECTOR = process.env.ELASTICIO_MESSAGE_CRYPTO_IV;
|
|
41
|
-
|
|
42
|
-
if (!PASSWORD) {
|
|
43
|
-
//mimic cypher
|
|
44
|
-
return new class extends PassThrough {
|
|
45
|
-
update(data) {
|
|
46
|
-
return data;
|
|
47
|
-
}
|
|
48
|
-
final() {
|
|
49
|
-
return '';
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!VECTOR) {
|
|
55
|
-
throw new Error('process.env.ELASTICIO_MESSAGE_CRYPTO_IV is not set');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const decodeKey = crypto.createHash('sha256').update(PASSWORD, 'utf-8').digest();
|
|
59
|
-
return crypto.createDecipheriv(ALGORYTHM, decodeKey, VECTOR);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function encryptIV(rawData, outputEncoding) {
|
|
63
|
-
debug('About to encrypt:', rawData);
|
|
64
|
-
|
|
65
|
-
if (!_.isString(rawData)) {
|
|
66
|
-
throw new Error('RabbitMQ message cipher.encryptIV() accepts only string as parameter.');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const cipher = createCypher();
|
|
70
|
-
return Buffer.concat([
|
|
71
|
-
Buffer.from(cipher.update(rawData, 'utf8', outputEncoding)),
|
|
72
|
-
Buffer.from(cipher.final(outputEncoding))
|
|
73
|
-
]);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function decryptIV(encData, inputEncoding) {
|
|
77
|
-
const decipher = createDecipher();
|
|
78
|
-
|
|
79
|
-
const data = inputEncoding ? encData.toString() : encData;
|
|
80
|
-
|
|
81
|
-
return decipher.update(data, inputEncoding, 'utf8') + decipher.final('utf8');
|
|
82
|
-
}
|
package/lib/component_reader.js
CHANGED
|
@@ -34,21 +34,31 @@ ComponentReader.prototype.promiseLoadJson = function promiseLoadJson(jsonFilePat
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
ComponentReader.prototype.
|
|
37
|
+
ComponentReader.prototype.findTriggerOrActionDefinition = function findTriggerOrActionDefinition(name) {
|
|
38
38
|
if (this.componentJson === null) {
|
|
39
39
|
throw new Error('Component.json was not loaded');
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
let def;
|
|
42
43
|
if (this.componentJson.triggers && this.componentJson.triggers[name]) {
|
|
43
|
-
|
|
44
|
+
def = this.componentJson.triggers[name];
|
|
44
45
|
} else if (this.componentJson.actions && this.componentJson.actions[name]) {
|
|
45
|
-
|
|
46
|
+
def = this.componentJson.actions[name];
|
|
46
47
|
} else {
|
|
47
48
|
throw new Error('Trigger or action "' + name + '" is not found in component.json!');
|
|
48
49
|
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
autoResolveObjectReferences: true,
|
|
53
|
+
...def
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
ComponentReader.prototype.findTriggerOrAction = function findTriggerOrAction(name) {
|
|
58
|
+
return this.findTriggerOrActionDefinition(name).main;
|
|
49
59
|
};
|
|
50
60
|
|
|
51
|
-
ComponentReader.prototype.loadTriggerOrAction = function loadTriggerOrAction(name) {
|
|
61
|
+
ComponentReader.prototype.loadTriggerOrAction = async function loadTriggerOrAction(name) {
|
|
52
62
|
var filename;
|
|
53
63
|
var modulePath;
|
|
54
64
|
var result;
|
package/lib/encryptor.js
CHANGED
|
@@ -1,21 +1,114 @@
|
|
|
1
|
-
const
|
|
1
|
+
const crypto = require('crypto');
|
|
2
2
|
const log = require('./logging.js');
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
class Encryptor {
|
|
5
|
+
constructor(password, iv) {
|
|
6
|
+
this._cryptoPassword = password;
|
|
7
|
+
this._cryptoIV = iv;
|
|
8
|
+
if (this._cryptoPassword) {
|
|
9
|
+
this._encryptionKey = crypto
|
|
10
|
+
.createHash('sha256')
|
|
11
|
+
.update(this._cryptoPassword, 'utf-8')
|
|
12
|
+
.digest();
|
|
13
|
+
if (!this._cryptoIV) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`missing crypt initialiazation vector,
|
|
16
|
+
most likely ELASTICIO_MESSAGE_CRYPTO_PASSWORD env var is not set
|
|
17
|
+
`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
this._algorithm = 'aes-256-cbc';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
createCipher() {
|
|
25
|
+
return crypto.createCipheriv(
|
|
26
|
+
this._algorithm,
|
|
27
|
+
this._encryptionKey,
|
|
28
|
+
this._cryptoIV
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
createDecipher() {
|
|
33
|
+
return crypto.createDecipheriv(
|
|
34
|
+
this._algorithm,
|
|
35
|
+
this._encryptionKey,
|
|
36
|
+
this._cryptoIV
|
|
37
|
+
);
|
|
38
|
+
}
|
|
6
39
|
|
|
7
|
-
function encryptMessageContent(messagePayload, outputEncoding) {
|
|
8
|
-
return cipher.encrypt(JSON.stringify(messagePayload), outputEncoding);
|
|
9
|
-
}
|
|
10
40
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Encrypt message to proper format
|
|
43
|
+
* @param {*} messagePayload anything json-stringifiable
|
|
44
|
+
* @param {'hex'|'base64'|'utf-8',undefined} outputEncoding
|
|
45
|
+
* @returns {Buffer}
|
|
46
|
+
*/
|
|
47
|
+
encryptMessageContent(messagePayload, outputEncoding) {
|
|
48
|
+
const encryptedBuffer = this._encryptToBuffer(JSON.stringify(messagePayload));
|
|
49
|
+
if (outputEncoding) {
|
|
50
|
+
return Buffer.from(encryptedBuffer.toString(outputEncoding));
|
|
51
|
+
} else {
|
|
52
|
+
return encryptedBuffer;
|
|
53
|
+
}
|
|
14
54
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Encrypt message to proper format
|
|
58
|
+
* @param {Buffer} messagePayload
|
|
59
|
+
* @param {'hex'|'base64'|'utf-8',undefined} inputEncoding
|
|
60
|
+
* @returns {*} anything, what have been encrypted
|
|
61
|
+
*/
|
|
62
|
+
decryptMessageContent(messagePayload, inputEncoding) {
|
|
63
|
+
if (!messagePayload || messagePayload.length === 0) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
let encryptedBuffer;
|
|
67
|
+
if (inputEncoding) {
|
|
68
|
+
encryptedBuffer = Buffer.from(messagePayload.toString(), inputEncoding);
|
|
69
|
+
} else {
|
|
70
|
+
encryptedBuffer = messagePayload;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const decryptedMessage = this._decryptFromBuffer(encryptedBuffer);
|
|
74
|
+
return JSON.parse(decryptedMessage);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
log.error(err, 'Failed to decrypt message');
|
|
77
|
+
throw Error('Failed to decrypt message: ' + err.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Encrypt payload.
|
|
83
|
+
* @param {Buffer|String} message
|
|
84
|
+
* @returns {Buffer}
|
|
85
|
+
*/
|
|
86
|
+
_encryptToBuffer(message) {
|
|
87
|
+
if (!this._encryptionKey) {
|
|
88
|
+
return Buffer.from(message);
|
|
89
|
+
}
|
|
90
|
+
const cipher = this.createCipher();
|
|
91
|
+
return Buffer.concat([
|
|
92
|
+
cipher.update(message),
|
|
93
|
+
cipher.final()
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Decrypt payload.
|
|
99
|
+
* @param {Buffer} message
|
|
100
|
+
* @returns {String}
|
|
101
|
+
*/
|
|
102
|
+
_decryptFromBuffer(message) {
|
|
103
|
+
if (!this._encryptionKey) {
|
|
104
|
+
return message.toString();
|
|
105
|
+
}
|
|
106
|
+
const decipher = this.createDecipher();
|
|
107
|
+
|
|
108
|
+
return Buffer.concat([
|
|
109
|
+
decipher.update(message),
|
|
110
|
+
decipher.final()
|
|
111
|
+
]).toString('utf8');
|
|
20
112
|
}
|
|
21
113
|
}
|
|
114
|
+
module.exports = Encryptor;
|
package/lib/executor.js
CHANGED
|
@@ -20,7 +20,7 @@ class TaskExec extends EventEmitter {
|
|
|
20
20
|
|
|
21
21
|
process(triggerOrAction, payload, cfg, snapshot) {
|
|
22
22
|
const onError = async (err) => {
|
|
23
|
-
this.logger.error(
|
|
23
|
+
this.logger.error('Error occurred during trigger or action processing');
|
|
24
24
|
await this.emit('error', err);
|
|
25
25
|
await this.emit('end');
|
|
26
26
|
};
|
package/lib/ipc.js
ADDED
package/lib/sailor.js
CHANGED
|
@@ -4,8 +4,8 @@ const amqp = require('./amqp.js');
|
|
|
4
4
|
const TaskExec = require('./executor.js').TaskExec;
|
|
5
5
|
const log = require('./logging.js');
|
|
6
6
|
const _ = require('lodash');
|
|
7
|
-
const cipher = require('./cipher.js');
|
|
8
7
|
const hooksData = require('./hooksData');
|
|
8
|
+
const Encryptor = require('../lib/encryptor');
|
|
9
9
|
const RestApiClient = require('elasticio-rest-node');
|
|
10
10
|
const assert = require('assert');
|
|
11
11
|
const co = require('co');
|
|
@@ -50,7 +50,11 @@ class Sailor {
|
|
|
50
50
|
jwtSecret: settings.OBJECT_STORAGE_TOKEN
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
const encryptor = new Encryptor(settings.MESSAGE_CRYPTO_PASSWORD, settings.MESSAGE_CRYPTO_IV);
|
|
54
|
+
this.objectStorage = objectStorage.use(
|
|
55
|
+
() => encryptor.createCipher(),
|
|
56
|
+
() => encryptor.createDecipher()
|
|
57
|
+
);
|
|
54
58
|
|
|
55
59
|
this.throttles = {
|
|
56
60
|
// 100 Messages per Second
|
|
@@ -82,7 +86,7 @@ class Sailor {
|
|
|
82
86
|
} = this;
|
|
83
87
|
|
|
84
88
|
const stepData = await apiClient.tasks.retrieveStep(flowId, stepId);
|
|
85
|
-
log.debug('Received step data
|
|
89
|
+
log.debug('Received step data');
|
|
86
90
|
assert(stepData);
|
|
87
91
|
|
|
88
92
|
Object.assign(this, {
|
|
@@ -118,7 +122,7 @@ class Sailor {
|
|
|
118
122
|
return co(function* doStartup() {
|
|
119
123
|
log.debug('Starting up component');
|
|
120
124
|
const result = yield this.invokeModuleFunction('startup');
|
|
121
|
-
log.trace('Startup data'
|
|
125
|
+
log.trace('Startup data received');
|
|
122
126
|
const handle = hooksData.startup(this.settings);
|
|
123
127
|
try {
|
|
124
128
|
const state = _.isEmpty(result) ? {} : result;
|
|
@@ -189,7 +193,7 @@ class Sailor {
|
|
|
189
193
|
try {
|
|
190
194
|
return await this.processMessage(payload, message);
|
|
191
195
|
} catch (e) {
|
|
192
|
-
log.error(
|
|
196
|
+
log.error('Something very bad happened during message processing');
|
|
193
197
|
} finally {
|
|
194
198
|
if (this.shutdownCallback) {
|
|
195
199
|
if (this.messagesCount === 0) {
|
|
@@ -205,24 +209,22 @@ class Sailor {
|
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
208
|
-
scheduleShutdown() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
log.debug('scheduleShutdown – shutdown is already scheduled, do nothing');
|
|
212
|
-
return new Promise(resolve => this.shutdownCallback = resolve);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
yield this.amqpConnection.listenQueueCancel();
|
|
216
|
-
if (this.messagesCount === 0) {
|
|
217
|
-
// there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
|
|
218
|
-
log.debug('scheduleShutdown – about to shutdown immediately');
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
// at least one processMessage invocation is not finished yet
|
|
222
|
-
// let's return a Promise, which will be resolved by processMessageAndMaybeShutdownCallback
|
|
223
|
-
log.debug('scheduleShutdown – shutdown is scheduled');
|
|
212
|
+
async scheduleShutdown() {
|
|
213
|
+
if (this.shutdownCallback) {
|
|
214
|
+
log.debug('scheduleShutdown – shutdown is already scheduled, do nothing');
|
|
224
215
|
return new Promise(resolve => this.shutdownCallback = resolve);
|
|
225
|
-
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
await this.amqpConnection.stopConsume();
|
|
219
|
+
if (this.messagesCount === 0) {
|
|
220
|
+
// there is no unfinished processMessage invocation, let's just resolve scheduleShutdown now
|
|
221
|
+
log.debug('scheduleShutdown – about to shutdown immediately');
|
|
222
|
+
return Promise.resolve();
|
|
223
|
+
}
|
|
224
|
+
// at least one processMessage invocation is not finished yet
|
|
225
|
+
// let's return a Promise, which will be resolved by processMessageAndMaybeShutdownCallback
|
|
226
|
+
log.debug('scheduleShutdown – shutdown is scheduled');
|
|
227
|
+
return new Promise(resolve => this.shutdownCallback = resolve);
|
|
226
228
|
}
|
|
227
229
|
|
|
228
230
|
|
|
@@ -257,17 +259,17 @@ class Sailor {
|
|
|
257
259
|
async fetchMessageBody(message, logger) {
|
|
258
260
|
const { body, headers } = message;
|
|
259
261
|
|
|
260
|
-
logger.info('Checking if incoming messages is lightweight...'
|
|
262
|
+
logger.info('Checking if incoming messages is lightweight...');
|
|
261
263
|
|
|
262
264
|
if (!headers) {
|
|
263
|
-
logger.info('Empty headers so not lightweight.'
|
|
265
|
+
logger.info('Empty headers so not lightweight.');
|
|
264
266
|
return body;
|
|
265
267
|
}
|
|
266
268
|
|
|
267
269
|
const { [OBJECT_ID_HEADER]: objectId } = headers;
|
|
268
270
|
|
|
269
271
|
if (!objectId) {
|
|
270
|
-
logger.
|
|
272
|
+
logger.trace('No object id header so not lightweight.');
|
|
271
273
|
return body;
|
|
272
274
|
}
|
|
273
275
|
|
|
@@ -278,43 +280,27 @@ class Sailor {
|
|
|
278
280
|
logger.info('Going to fetch message body.', { objectId });
|
|
279
281
|
|
|
280
282
|
try {
|
|
281
|
-
object = await this.objectStorage.getAsJSON(
|
|
283
|
+
object = await this.objectStorage.getAsJSON(
|
|
284
|
+
objectId,
|
|
285
|
+
{ jwtPayloadOrToken: this.settings.OBJECT_STORAGE_TOKEN }
|
|
286
|
+
);
|
|
282
287
|
} catch (e) {
|
|
283
288
|
log.error(e);
|
|
284
289
|
throw new Error(`Failed to get message body with id=${objectId}`);
|
|
285
290
|
}
|
|
286
291
|
|
|
287
292
|
logger.info('Successfully obtained message body.', { objectId });
|
|
288
|
-
logger.trace('Message body object'
|
|
293
|
+
logger.trace('Message body object received');
|
|
289
294
|
|
|
290
295
|
return object;
|
|
291
296
|
}
|
|
292
297
|
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
logger.info('Message size is below threshold', { size, OBJECT_STORAGE_SIZE_THRESHOLD });
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const readable = Readable.from(buf.values());
|
|
304
|
-
|
|
305
|
-
let id;
|
|
306
|
-
|
|
307
|
-
logger.info('Message size is above threshold, going to upload', { size, OBJECT_STORAGE_SIZE_THRESHOLD });
|
|
308
|
-
|
|
309
|
-
try {
|
|
310
|
-
id = await this.objectStorage.addAsStream(readable);
|
|
311
|
-
} catch (e) {
|
|
312
|
-
log.error(e);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
logger.info('Message uploaded', { id });
|
|
316
|
-
|
|
317
|
-
return id;
|
|
298
|
+
uploadMessageBody(bodyBuf) {
|
|
299
|
+
const stream = () => Readable.from(bodyBuf);
|
|
300
|
+
return this.objectStorage.addAsStream(
|
|
301
|
+
stream,
|
|
302
|
+
{ jwtPayloadOrToken: this.settings.OBJECT_STORAGE_TOKEN }
|
|
303
|
+
);
|
|
318
304
|
}
|
|
319
305
|
|
|
320
306
|
async runExec(module, payload, message, outgoingMessageHeaders, stepData, timeStart, logger) {
|
|
@@ -376,49 +362,89 @@ class Sailor {
|
|
|
376
362
|
}
|
|
377
363
|
|
|
378
364
|
data.headers = data.headers || {};
|
|
379
|
-
const { body, passthrough } = data;
|
|
365
|
+
const { body, passthrough = {} } = data;
|
|
380
366
|
|
|
381
367
|
if (settings.EMIT_LIGHTWEIGHT_MESSAGE) {
|
|
382
|
-
logger.
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
368
|
+
logger.trace('Outgoing lightweight is enabled, going to check size.');
|
|
369
|
+
const bodyBuf = Buffer.from(JSON.stringify(body), 'utf-8');
|
|
370
|
+
const passthroughBufs = Object.keys(passthrough).map(stepId => ({
|
|
371
|
+
stepId,
|
|
372
|
+
body: Buffer.from(JSON.stringify(passthrough[stepId].body), 'utf-8'),
|
|
373
|
+
id: passthrough[stepId].headers && passthrough[stepId].headers[OBJECT_ID_HEADER]
|
|
374
|
+
}));
|
|
375
|
+
|
|
376
|
+
const totalLength = passthroughBufs.reduce((len, { body }) =>
|
|
377
|
+
len + body.length, bodyBuf.length);
|
|
378
|
+
|
|
379
|
+
if (totalLength > settings.OBJECT_STORAGE_SIZE_THRESHOLD) {
|
|
380
|
+
logger.info(
|
|
381
|
+
'Message size is above threshold, going to upload',
|
|
382
|
+
{
|
|
383
|
+
totalLength,
|
|
384
|
+
OBJECT_STORAGE_SIZE_THRESHOLD: settings.OBJECT_STORAGE_SIZE_THRESHOLD
|
|
394
385
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
'Passthrough is lightweight already',
|
|
406
|
-
{ stepId, headers }
|
|
407
|
-
);
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
const id = await that.uploadMessageBody(data.passthrough[stepId].body, logger);
|
|
411
|
-
if (id) {
|
|
412
|
-
data.passthrough[stepId].body = {};
|
|
413
|
-
data.passthrough[stepId].headers = {
|
|
414
|
-
...(headers || {}),
|
|
415
|
-
[OBJECT_ID_HEADER]: id
|
|
416
|
-
};
|
|
417
|
-
}
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
let bodyId;
|
|
389
|
+
let passthroughIds;
|
|
390
|
+
try {
|
|
391
|
+
[bodyId, ...passthroughIds] = await Promise.all([
|
|
392
|
+
that.uploadMessageBody(bodyBuf),
|
|
393
|
+
...passthroughBufs.map(async ({ stepId, body, id }) => {
|
|
394
|
+
const bodyId = id || await that.uploadMessageBody(body);
|
|
395
|
+
return { stepId, bodyId };
|
|
418
396
|
})
|
|
419
|
-
|
|
420
|
-
)
|
|
421
|
-
|
|
397
|
+
]);
|
|
398
|
+
} catch (e) {
|
|
399
|
+
logger.error(e, 'Error during message/passthrough body upload');
|
|
400
|
+
return onError(new Error('Lightweight message/passthrough body upload error'));
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
logger.info('Message body uploaded', { id: bodyId });
|
|
404
|
+
const { headers } = data;
|
|
405
|
+
data.body = {};
|
|
406
|
+
data.headers = {
|
|
407
|
+
...(headers || {}),
|
|
408
|
+
[OBJECT_ID_HEADER]: bodyId
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
for (const { stepId, bodyId } of passthroughIds) {
|
|
412
|
+
logger.info('Passthrough Message body uploaded', { stepId, id: bodyId });
|
|
413
|
+
const { [stepId]: { headers } } = passthrough;
|
|
414
|
+
data.passthrough[stepId].body = {};
|
|
415
|
+
data.passthrough[stepId].headers = {
|
|
416
|
+
...(headers || {}),
|
|
417
|
+
[OBJECT_ID_HEADER]: bodyId
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
} else {
|
|
422
|
+
logger.trace(
|
|
423
|
+
'Message size is below threshold.',
|
|
424
|
+
{
|
|
425
|
+
totalLength,
|
|
426
|
+
OBJECT_STORAGE_SIZE_THRESHOLD: settings.OBJECT_STORAGE_SIZE_THRESHOLD
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
} else if (passthrough) {
|
|
431
|
+
logger.trace('Outgoing lightweight is disabled, going to download all bodies.');
|
|
432
|
+
try {
|
|
433
|
+
await Promise.all(Object.keys(passthrough).map(async stepId => {
|
|
434
|
+
logger.trace('Going to check if passthrough for step is lightweight.', { stepId });
|
|
435
|
+
// if body is not empty then we've downloaded before processing, no need to redownload
|
|
436
|
+
if (!_.isEmpty(data.passthrough[stepId].body)) {
|
|
437
|
+
logger.trace('Body is not empty.', { stepId });
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
data.passthrough[stepId].body = await that.fetchMessageBody(
|
|
441
|
+
passthrough[stepId],
|
|
442
|
+
logger
|
|
443
|
+
);
|
|
444
|
+
}));
|
|
445
|
+
} catch (e) {
|
|
446
|
+
return onError(e);
|
|
447
|
+
}
|
|
422
448
|
}
|
|
423
449
|
|
|
424
450
|
if (stepData.is_passthrough === true && !settings.NO_SELF_PASSTRHOUGH) {
|
|
@@ -427,8 +453,11 @@ class Sailor {
|
|
|
427
453
|
});
|
|
428
454
|
}
|
|
429
455
|
|
|
456
|
+
log.trace('Going to send outgoing message');
|
|
457
|
+
|
|
430
458
|
try {
|
|
431
459
|
await that.amqpConnection.sendData(data, headers, that.throttles.data);
|
|
460
|
+
log.trace('Outgoing message sent');
|
|
432
461
|
} catch (err) {
|
|
433
462
|
return onError(err);
|
|
434
463
|
}
|
|
@@ -457,16 +486,13 @@ class Sailor {
|
|
|
457
486
|
}
|
|
458
487
|
|
|
459
488
|
async function onRebound(err) {
|
|
460
|
-
const headers = _.clone(outgoingMessageHeaders);
|
|
461
489
|
err = formatError(err);
|
|
462
490
|
logger.trace({
|
|
463
491
|
err,
|
|
464
492
|
messagesCount: that.messagesCount,
|
|
465
493
|
messageProcessingTime: Date.now() - timeStart
|
|
466
494
|
}, 'processMessage emit rebound');
|
|
467
|
-
|
|
468
|
-
headers.reboundReason = err.message;
|
|
469
|
-
return that.amqpConnection.sendRebound(err, message, headers);
|
|
495
|
+
return that.amqpConnection.sendRebound(err, message);
|
|
470
496
|
}
|
|
471
497
|
|
|
472
498
|
async function onSnapshot(data) {
|
|
@@ -528,7 +554,6 @@ class Sailor {
|
|
|
528
554
|
errorCount: taskExec.errorCount,
|
|
529
555
|
messageProcessingTime: Date.now() - timeStart
|
|
530
556
|
}, 'processMessage emit end');
|
|
531
|
-
|
|
532
557
|
resolve();
|
|
533
558
|
}
|
|
534
559
|
});
|
|
@@ -589,8 +614,7 @@ class Sailor {
|
|
|
589
614
|
stepId: settings.STEP_ID,
|
|
590
615
|
compId: settings.COMP_ID,
|
|
591
616
|
function: settings.FUNCTION,
|
|
592
|
-
start: new Date().getTime()
|
|
593
|
-
cid: cipher.id
|
|
617
|
+
start: new Date().getTime()
|
|
594
618
|
};
|
|
595
619
|
let module;
|
|
596
620
|
try {
|
|
@@ -603,30 +627,34 @@ class Sailor {
|
|
|
603
627
|
return;
|
|
604
628
|
}
|
|
605
629
|
|
|
606
|
-
const
|
|
630
|
+
const method = this.componentReader.findTriggerOrActionDefinition(settings.FUNCTION);
|
|
607
631
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
logger
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
632
|
+
if (method.autoResolveObjectReferences) {
|
|
633
|
+
const { passthrough } = payload;
|
|
634
|
+
|
|
635
|
+
try {
|
|
636
|
+
await Promise.all([
|
|
637
|
+
(async () => {
|
|
638
|
+
logger.trace('Going to check if incoming message body is lightweight.');
|
|
639
|
+
payload.body = await this.fetchMessageBody(payload, logger);
|
|
640
|
+
})(),
|
|
641
|
+
...(passthrough
|
|
642
|
+
? Object.keys(passthrough).map(async stepId => {
|
|
643
|
+
logger.trace('Going to check if passthrough for step is lightweight.', { stepId });
|
|
644
|
+
payload.passthrough[stepId].body = await this.fetchMessageBody(
|
|
645
|
+
payload.passthrough[stepId],
|
|
646
|
+
logger
|
|
647
|
+
);
|
|
648
|
+
})
|
|
649
|
+
: [])
|
|
650
|
+
]);
|
|
651
|
+
} catch (e) {
|
|
652
|
+
logger.error(e);
|
|
653
|
+
outgoingMessageHeaders.end = new Date().getTime();
|
|
654
|
+
self.amqpConnection.sendError(e, outgoingMessageHeaders, message);
|
|
655
|
+
self.amqpConnection.reject(message);
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
630
658
|
}
|
|
631
659
|
|
|
632
660
|
await this.runExec(module, payload, message, outgoingMessageHeaders, stepData, timeStart, logger);
|