diff --git a/bin/server b/bin/server index 3fcb18115..3ec48c917 100755 --- a/bin/server +++ b/bin/server @@ -50,6 +50,9 @@ const notificationConfig = config.get('notifications'); // Multiple build cluster feature flag const multiBuildClusterEnabled = convertToBool(config.get('multiBuildCluster').enabled); +// Queue Webhook feature flag +const queueWebhookEnabled = convertToBool(config.get('queueWebhook').enabled); + // Default cluster environment variable const clusterEnvConfig = config.get('build').environment; // readonly const clusterEnv = { ...clusterEnvConfig }; @@ -254,6 +257,10 @@ datastore.setup(datastoreConfig.ddlSyncEnabled).then(() => validator: { externalJoin: true, notificationsValidationErr + }, + queueWebhook: { + executor, + queueWebhookEnabled } }) .then(instance => logger.info('Server running at %s', instance.info.uri)) diff --git a/config/custom-environment-variables.yaml b/config/custom-environment-variables.yaml index 2ed755d89..5f2f0e4db 100644 --- a/config/custom-environment-variables.yaml +++ b/config/custom-environment-variables.yaml @@ -271,6 +271,11 @@ executor: tls: QUEUE_REDIS_TLS_ENABLED database: QUEUE_REDIS_DATABASE + +queueWebhook: + # Enabled events from webhook queue or not + enabled: QUEUE_WEBHOOK_ENABLED + scms: __name: SCM_SETTINGS __format: json diff --git a/config/default.yaml b/config/default.yaml index 0da84e98b..d68830b39 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -190,6 +190,10 @@ executor: tls: false database: 0 +queueWebhook: + # Enabled events from webhook queue or not + enabled: false + scms: {} # github: # plugin: github diff --git a/lib/server.js b/lib/server.js index 346396d27..080f795d4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -136,7 +136,8 @@ module.exports = async config => { collectionFactory: config.collectionFactory, buildClusterFactory: config.buildClusterFactory, ecosystem: config.ecosystem, - release: config.release + release: config.release, + queueWebhook: config.queueWebhook }; const bellConfigs = await config.auth.scm.getBellConfiguration(); diff --git a/plugins/webhooks/index.js b/plugins/webhooks/index.js index 8809171b6..3a5a43458 100644 --- a/plugins/webhooks/index.js +++ b/plugins/webhooks/index.js @@ -62,9 +62,11 @@ const webhooksPlugin = { maxBytes: parseInt(pluginOptions.maxBytes, 10) || DEFAULT_MAX_BYTES }, handler: async (request, h) => { - const { pipelineFactory } = request.server.app; + const { pipelineFactory, queueWebhook } = request.server.app; const { scm } = pipelineFactory; + const { executor, queueWebhookEnabled } = queueWebhook; let message = 'Unable to process this kind of event'; + let hookId; try { const parsed = await scm.parseHook(request.headers, request.payload); @@ -76,10 +78,26 @@ const webhooksPlugin = { parsed.pluginOptions = pluginOptions; - const { type, hookId } = parsed; + const { type } = parsed; + hookId = parsed.hookId; request.log(['webhook', hookId], `Received event type ${type}`); + if (queueWebhookEnabled) { + parsed.token = request.server.plugins.auth.generateToken({ + scope: ['sdapi'] + }); + + try { + return await executor.enqueueWebhook(parsed); + } catch (err) { + // if enqueueWebhook is not implemented, an event starts without enqueuing + if (err.message != 'Not implemented') { + throw err; + } + } + } + return await startHookEvent(request, h, parsed); } catch (err) { diff --git a/test/plugins/webhooks.test.js b/test/plugins/webhooks.test.js index 654f8ee79..943ff26f1 100644 --- a/test/plugins/webhooks.test.js +++ b/test/plugins/webhooks.test.js @@ -31,6 +31,7 @@ describe('webhooks plugin test', () => { let pipelineFactoryMock; let userFactoryMock; let eventFactoryMock; + let queueWebhookMock; let plugin; let server; const apiUri = 'http://foo.bar:12345'; @@ -72,6 +73,12 @@ describe('webhooks plugin test', () => { }, create: sinon.stub() }; + queueWebhookMock = { + executor: { + enqueueWebhook: sinon.stub() + }, + queueWebhookEnabled: false + } plugin = rewire('../../plugins/webhooks'); @@ -85,8 +92,14 @@ describe('webhooks plugin test', () => { buildFactory: buildFactoryMock, pipelineFactory: pipelineFactoryMock, userFactory: userFactoryMock, - eventFactory: eventFactoryMock + eventFactory: eventFactoryMock, + queueWebhook: queueWebhookMock }; + server.plugins = { + auth: { + generateToken: () => 'iamtoken' + } + } await server.register({ plugin, @@ -132,7 +145,8 @@ describe('webhooks plugin test', () => { buildFactory: buildFactoryMock, pipelineFactory: pipelineFactoryMock, userFactory: userFactoryMock, - eventFactory: eventFactoryMock + eventFactory: eventFactoryMock, + queueWebhook: queueWebhookMock }; assert.isRejected( @@ -1334,6 +1348,29 @@ describe('webhooks plugin test', () => { }); }); + + it('calls enqueueWebhook with webhookConfig when queueWebhookEnabled is true', () => { + queueWebhookMock.queueWebhookEnabled = true; + queueWebhookMock.executor.enqueueWebhook.resolves(null); + + return server.inject(options).then(() => { + assert.calledWith(queueWebhookMock.executor.enqueueWebhook, { + ...parsed, + token: 'iamtoken' + }); + }); + }); + + it('returns 201 when executor.enqueueWebhook is not implemented', () => { + const err = new Error('Not implemented'); + queueWebhookMock.queueWebhookEnabled = true; + queueWebhookMock.executor.enqueueWebhook.rejects(err); + + return server.inject(options).then(reply => { + assert.equal(reply.statusCode, 201); + }); + }); + it('returns 500 when failed', () => { eventFactoryMock.create.rejects(new Error('Failed to start')); @@ -1747,7 +1784,8 @@ describe('webhooks plugin test', () => { buildFactory: buildFactoryMock, pipelineFactory: pipelineFactoryMock, userFactory: userFactoryMock, - eventFactory: eventFactoryMock + eventFactory: eventFactoryMock, + queueWebhook: queueWebhookMock }; testServer.register([ @@ -2289,7 +2327,8 @@ describe('webhooks plugin test', () => { buildFactory: buildFactoryMock, pipelineFactory: pipelineFactoryMock, userFactory: userFactoryMock, - eventFactory: eventFactoryMock + eventFactory: eventFactoryMock, + queueWebhook: queueWebhookMock }; testServer.register([