screwdriver-queue-service 2.0.16 → 2.0.20

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/lib/queue.js CHANGED
@@ -35,6 +35,7 @@ module.exports = class ExecutorQueue {
35
35
  this.timeoutQueue = `${this.prefix}timeoutConfigs`;
36
36
  this.cacheQueue = `${this.prefix}cache`;
37
37
  this.unzipQueue = `${this.prefix}unzip`;
38
+ this.webhookQueue = `${this.prefix}webhooks`;
38
39
 
39
40
  const redisConnection = { ...config.redisConnection, pkg: 'ioredis' };
40
41
 
@@ -57,20 +58,6 @@ module.exports = class ExecutorQueue {
57
58
  this.redis[funcName](...args),
58
59
  breakerOptions
59
60
  );
60
- this.requestRetryStrategy = response => {
61
- if (Math.floor(response.statusCode / 100) !== 2) {
62
- throw new Error('Retry limit reached');
63
- }
64
-
65
- return response;
66
- };
67
- this.requestRetryStrategyPostEvent = response => {
68
- if (Math.floor(response.statusCode / 100) !== 2 && response.statusCode !== 404) {
69
- throw new Error('Retry limit reached');
70
- }
71
-
72
- return response;
73
- };
74
61
  this.fuseBox = new FuseBox();
75
62
  this.fuseBox.addFuse(this.queueBreaker);
76
63
  this.fuseBox.addFuse(this.redisBreaker);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "screwdriver-queue-service",
3
- "version": "2.0.16",
3
+ "version": "2.0.20",
4
4
  "description": "Screwdriver Queue Service API",
5
5
  "main": "app.js",
6
6
  "directories": {
@@ -11,17 +11,17 @@
11
11
  "@hapi/good": "^9.0.1",
12
12
  "@hapi/good-console": "^9.0.1",
13
13
  "@hapi/good-squeeze": "^6.0.0",
14
- "@hapi/hapi": "^20.1.0",
14
+ "@hapi/hapi": "^20.2.1",
15
15
  "@hapi/hoek": "^9.1.1",
16
- "amqp-connection-manager": "^3.2.2",
16
+ "amqp-connection-manager": "^3.8.1",
17
17
  "amqplib": "^0.8.0",
18
18
  "blipp": "^4.0.2",
19
19
  "circuit-fuses": "^4.0.5",
20
20
  "config": "^1.31.0",
21
- "cron-parser": "^2.18.0",
21
+ "cron-parser": "^4.2.1",
22
22
  "hapi-auth-jwt2": "^10.2.0",
23
23
  "ioredis": "^3.2.2",
24
- "joi": "^17.4.0",
24
+ "joi": "^17.5.0",
25
25
  "js-yaml": "^3.14.1",
26
26
  "jsonwebtoken": "^8.5.1",
27
27
  "laabr": "^6.1.3",
@@ -29,10 +29,10 @@
29
29
  "npm-auto-version": "^1.0.0",
30
30
  "redlock": "^4.2.0",
31
31
  "screwdriver-aws-producer-service": "^1.1.0",
32
- "screwdriver-data-schema": "^21.10.2",
32
+ "screwdriver-data-schema": "^21.16.0",
33
33
  "screwdriver-executor-docker": "^5.0.2",
34
34
  "screwdriver-executor-jenkins": "^5.0.1",
35
- "screwdriver-executor-k8s": "^14.14.4",
35
+ "screwdriver-executor-k8s": "^14.16.0",
36
36
  "screwdriver-executor-k8s-vm": "^4.3.2",
37
37
  "screwdriver-executor-router": "^2.1.2",
38
38
  "screwdriver-logger": "^1.0.2",
@@ -53,7 +53,7 @@
53
53
  "mockery": "^2.1.0",
54
54
  "nyc": "^15.1.0",
55
55
  "sinon": "^9.2.4",
56
- "snyk": "^1.712.0",
56
+ "snyk": "^1.814.0",
57
57
  "util": "^0.12.2"
58
58
  },
59
59
  "scripts": {
package/plugins/helper.js CHANGED
@@ -7,6 +7,34 @@ const { queuePrefix } = require('../config/redis');
7
7
  const RETRY_LIMIT = 3;
8
8
  const RETRY_DELAY = 5;
9
9
 
10
+ /**
11
+ * Callback function to retry when HTTP status code is not 2xx
12
+ * @param {Object} response
13
+ * @param {Function} retryWithMergedOptions
14
+ * @return {Object} response
15
+ */
16
+ function requestRetryStrategy(response) {
17
+ if (Math.floor(response.statusCode / 100) !== 2) {
18
+ throw new Error('Retry limit reached');
19
+ }
20
+
21
+ return response;
22
+ }
23
+
24
+ /**
25
+ * Callback function to retry when HTTP status code is not 2xx and 404
26
+ * @param {Object} response
27
+ * @param {Function} retryWithMergedOptions
28
+ * @return {Object} response
29
+ */
30
+ function requestRetryStrategyPostEvent(response) {
31
+ if (Math.floor(response.statusCode / 100) !== 2 && response.statusCode !== 404) {
32
+ throw new Error('Retry limit reached');
33
+ }
34
+
35
+ return response;
36
+ }
37
+
10
38
  /**
11
39
  *
12
40
  * @param {String} method
@@ -218,11 +246,36 @@ async function updateBuild(updateConfig, retryStrategyFn) {
218
246
  );
219
247
  }
220
248
 
249
+ /**
250
+ * Post the webhooks process
251
+ * @method processHooks
252
+ * @param {String} apiUri
253
+ * @param {String} token
254
+ * @param {String} webhookConfig as JSON format
255
+ * @param {Function} retryStrategyFn
256
+ * @return {Promise} response or error
257
+ */
258
+ async function processHooks(apiUri, token, webhookConfig, retryStrategyFn) {
259
+ return request(formatOptions('POST', `${apiUri}/v4/processHooks`, token, webhookConfig, retryStrategyFn)).then(
260
+ res => {
261
+ logger.info(`POST /v4/processHooks completed, ${res.statusCode}, ${JSON.stringify(res.body)}`);
262
+ if ([200, 201, 204].includes(res.statusCode)) {
263
+ return res;
264
+ }
265
+
266
+ throw new Error(`Failed to process webhook with ${res.statusCode} code and ${res.body}`);
267
+ }
268
+ );
269
+ }
270
+
221
271
  module.exports = {
272
+ requestRetryStrategy,
273
+ requestRetryStrategyPostEvent,
222
274
  updateBuildStatus,
223
275
  updateStepStop,
224
276
  getCurrentStep,
225
277
  createBuildEvent,
226
278
  getPipelineAdmin,
227
- updateBuild
279
+ updateBuild,
280
+ processHooks
228
281
  };
@@ -36,6 +36,9 @@ module.exports = () => ({
36
36
  case 'unzip':
37
37
  await scheduler.unzipArtifacts(executor, request.payload);
38
38
  break;
39
+ case 'webhook':
40
+ await scheduler.queueWebhook(executor, request.payload);
41
+ break;
39
42
  default:
40
43
  await scheduler.start(executor, request.payload);
41
44
  break;
@@ -38,7 +38,7 @@ async function postBuildEvent(executor, eventConfig) {
38
38
  scope: ['user']
39
39
  });
40
40
 
41
- const admin = await helper.getPipelineAdmin(token, apiUri, pipelineId, executor.requestRetryStrategy);
41
+ const admin = await helper.getPipelineAdmin(token, apiUri, pipelineId, helper.requestRetryStrategy);
42
42
 
43
43
  if (admin) {
44
44
  logger.info(
@@ -64,7 +64,7 @@ async function postBuildEvent(executor, eventConfig) {
64
64
  buildEvent.buildId = buildId;
65
65
  }
66
66
 
67
- await helper.createBuildEvent(apiUri, jwt, buildEvent, executor.requestRetryStrategyPostEvent);
67
+ await helper.createBuildEvent(apiUri, jwt, buildEvent, helper.requestRetryStrategyPostEvent);
68
68
  } else {
69
69
  logger.error(
70
70
  `POST event for pipeline failed as no admin found: ${pipelineId}:${job.name}:${job.id}:${buildId}`
@@ -337,7 +337,7 @@ async function start(executor, config) {
337
337
  apiUri,
338
338
  payload
339
339
  },
340
- executor.requestRetryStrategy
340
+ helper.requestRetryStrategy
341
341
  )
342
342
  .catch(err => {
343
343
  logger.error(`frozenBuilds: failed to update build status for build ${buildId}: ${err}`);
@@ -399,7 +399,7 @@ async function start(executor, config) {
399
399
  apiUri,
400
400
  payload: { stats: build.stats, status: 'QUEUED' }
401
401
  },
402
- executor.requestRetryStrategy
402
+ helper.requestRetryStrategy
403
403
  );
404
404
  }
405
405
  }
@@ -716,6 +716,30 @@ async function unzipArtifacts(executor, config) {
716
716
  return enq;
717
717
  }
718
718
 
719
+ /**
720
+ * Pushes webhooks to redis
721
+ * @async queueWebhook
722
+ * @param {Object} executor
723
+ * @param {Object} webhookConfig
724
+ * @return {Promise}
725
+ */
726
+ async function queueWebhook(executor, webhookConfig) {
727
+ await executor.connect();
728
+
729
+ return executor.queueBreaker.runCommand(
730
+ 'enqueue',
731
+ executor.webhookQueue,
732
+ 'sendWebhook',
733
+ JSON.stringify({
734
+ webhookConfig,
735
+ token: executor.tokenGen({
736
+ service: 'queue',
737
+ scope: ['webhook_worker']
738
+ })
739
+ })
740
+ );
741
+ }
742
+
719
743
  module.exports = {
720
744
  init,
721
745
  start,
@@ -728,5 +752,6 @@ module.exports = {
728
752
  stopTimer,
729
753
  cleanUp,
730
754
  clearCache,
731
- unzipArtifacts
755
+ unzipArtifacts,
756
+ queueWebhook
732
757
  };
@@ -74,12 +74,11 @@ const timeOutOfWindow = (cronExp, timeToCheck) => {
74
74
  const utcDayOfWeek = timeToCheck.getUTCDay();
75
75
  const utcMonth = timeToCheck.getUTCMonth() + 1;
76
76
 
77
- /* eslint no-underscore-dangle: ["error", { "allow": ["_fields"] }] */
78
- const minuteField = cronObj._fields.minute;
79
- const hourField = cronObj._fields.hour;
80
- const dayOfMonthField = cronObj._fields.dayOfMonth;
81
- const dayOfWeekField = cronObj._fields.dayOfWeek;
82
- const monthField = cronObj._fields.month;
77
+ const minuteField = cronObj.fields.minute;
78
+ const hourField = cronObj.fields.hour;
79
+ const dayOfMonthField = cronObj.fields.dayOfMonth;
80
+ const dayOfWeekField = cronObj.fields.dayOfWeek;
81
+ const monthField = cronObj.fields.month;
83
82
 
84
83
  const includesMinute = minuteField.includes(utcMinutes);
85
84
  const includesHour = hourField.includes(utcHours);
@@ -7,6 +7,7 @@ const hoek = require('@hapi/hoek');
7
7
  const ExecutorRouter = require('screwdriver-executor-router');
8
8
  const logger = require('screwdriver-logger');
9
9
  const AWSProducer = require('screwdriver-aws-producer-service');
10
+ const helper = require('../../helper');
10
11
  const { BlockedBy } = require('./BlockedBy');
11
12
  const { Filter } = require('./Filter');
12
13
  const { CacheFilter } = require('./CacheFilter');
@@ -17,7 +18,7 @@ const { amqpURI, exchange, connectOptions } = rabbitmqConf.getConfig();
17
18
  const kafkaEnabled = config.get('kafka').enabled === 'true';
18
19
 
19
20
  const RETRY_LIMIT = 3;
20
- // This is in milliseconds, reference: https://github.com/taskrabbit/node-resque/blob/master/lib/plugins/Retry.js#L12
21
+ // This is in milliseconds, reference: https://github.com/actionhero/node-resque/blob/2ffdf0/lib/plugins/Retry.js#L12
21
22
  const RETRY_DELAY = 5 * 1000;
22
23
  const redis = new Redis(connectionDetails.port, connectionDetails.host, connectionDetails.options);
23
24
 
@@ -273,6 +274,20 @@ async function clear(cacheConfig) {
273
274
  return null;
274
275
  }
275
276
 
277
+ /**
278
+ * Send message to processHooks API
279
+ * @param {String} configs as String
280
+ */
281
+ async function sendWebhook(configs) {
282
+ const parsedConfig = JSON.parse(configs);
283
+ const { webhookConfig, token } = parsedConfig;
284
+ const apiUri = ecosystem.api;
285
+
286
+ await helper.processHooks(apiUri, token, webhookConfig, helper.requestRetryStrategyPostEvent);
287
+
288
+ return null;
289
+ }
290
+
276
291
  module.exports = {
277
292
  start: {
278
293
  plugins: [Filter, 'Retry', BlockedBy],
@@ -295,5 +310,12 @@ module.exports = {
295
310
  Retry: retryOptions
296
311
  },
297
312
  perform: clear
313
+ },
314
+ sendWebhook: {
315
+ plugins: ['Retry'],
316
+ pluginOptions: {
317
+ Retry: retryOptions
318
+ },
319
+ perform: sendWebhook
298
320
  }
299
321
  };
@@ -42,7 +42,7 @@ async function shutDownAll(worker, scheduler) {
42
42
  const multiWorker = new NodeResque.MultiWorker(
43
43
  {
44
44
  connection: connectionDetails,
45
- queues: [`${queuePrefix}builds`, `${queuePrefix}cache`],
45
+ queues: [`${queuePrefix}builds`, `${queuePrefix}cache`, `${queuePrefix}webhooks`],
46
46
  minTaskProcessors: workerConfig.minTaskProcessors,
47
47
  maxTaskProcessors: workerConfig.maxTaskProcessors,
48
48
  checkTimeout: workerConfig.checkTimeout,