diff --git a/.eslintrc b/.eslintrc index 22a0c1e..1315e9d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -78,7 +78,6 @@ "quote-props": ["error", "consistent"], "promise/catch-or-return": ["error", { "allowThen": true }], - "promise/no-native": "error", "mocha/no-exclusive-tests": "error", diff --git a/lib/addNewMask.js b/lib/addNewMask.js index 78da91a..25604fa 100644 --- a/lib/addNewMask.js +++ b/lib/addNewMask.js @@ -1,18 +1,18 @@ const rp = require('request-promise'); +const { secretsServerAddress } = require('./logger'); function updateMasks(secret) { - const port = process.env.PORT || 8080; - const host = process.env.HOST || '0.0.0.0'; - - const opt = { - uri: `http://${host}:${port}/secrets`, - method: 'POST', - json: true, - body: secret, - resolveWithFullResponse: true, - }; - - rp(opt) + secretsServerAddress + .then((address) => { + const opts = { + uri: `${address}/secrets`, + method: 'POST', + json: true, + body: secret, + resolveWithFullResponse: true, + }; + return rp(opts); + }) .then((res) => { if (res.statusCode >= 400) { console.log(`could not create mask for secret: ${secret.key}, because server responded with: ${res.statusCode}\n\n${res.body}`); diff --git a/lib/helpers.js b/lib/helpers.js index 4c52d31..d30ca9a 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,7 +1,7 @@ -const Q = require('q'); const { stat } = require('fs/promises'); const path = require('path'); const logger = require('cf-logs').Logger('codefresh:containerLogger'); +const getPromiseWithResolvers = require('core-js-pure/es/promise/with-resolvers'); const { BuildFinishedSignalFilename } = require('./enums'); const checkFileInterval = 1000; @@ -27,7 +27,7 @@ function _watchForBuildFinishedSignal(deferred) { } function watchForBuildFinishedSignal() { - const deferred = Q.defer(); + const deferred = getPromiseWithResolvers(); _watchForBuildFinishedSignal(deferred); @@ -35,5 +35,10 @@ function watchForBuildFinishedSignal() { } module.exports = { + /** + * Polyfill of `Promise.withResolvers`, TC39 Stage 4 proposal. + * @see https://github.com/tc39/proposal-promise-with-resolvers + */ + getPromiseWithResolvers, watchForBuildFinishedSignal, }; diff --git a/lib/index.js b/lib/index.js index a608ac1..1ed69f2 100644 --- a/lib/index.js +++ b/lib/index.js @@ -12,7 +12,7 @@ const loggerOptions = { }; cflogs.init(loggerOptions); -const Logger = require('./logger'); +const { Logger } = require('./logger'); const { watchForBuildFinishedSignal } = require('./helpers'); const taskLoggerConfig = JSON.parse(process.env.TASK_LOGGER_CONFIG); diff --git a/lib/logger.js b/lib/logger.js index d747f4e..bd97679 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,7 +1,6 @@ const fs = require('fs'); const { EventEmitter } = require('events'); const _ = require('lodash'); -const Q = require('q'); const Docker = require('dockerode'); const DockerEvents = require('docker-events'); const CFError = require('cf-errors'); @@ -12,11 +11,14 @@ const { ContainerStatus } = require('./enums'); const { LoggerStrategy } = require('./enums'); const { ContainerHandlingStatus } = require('./enums'); const ContainerLogger = require('./ContainerLogger'); +const { getPromiseWithResolvers } = require('./helpers'); const initialState = { pid: process.pid, status: 'init', lastLogsDate: new Date(), failedHealthChecks: [], restartCounter: 0, containers: {} }; +const deferredServerAddress = getPromiseWithResolvers(); + class Logger { constructor({ @@ -34,7 +36,7 @@ class Logger { this.containerLoggers = []; this.totalLogSize = 0; this.taskLogger = undefined; - this.buildFinishedPromise = buildFinishedPromise || Q.resolve(); + this.buildFinishedPromise = buildFinishedPromise || Promise.resolve(); this.finishedContainers = 0; this.finishedContainersEmitter = new EventEmitter(); this.showProgress = showProgress; @@ -350,38 +352,45 @@ class Logger { } async _listenForEngineUpdates() { - const port = +(process.env.PORT || 8080); - const host = process.env.HOST || '0.0.0.0'; - - const secretsServer = fastify(); - const secretsOptions = { - schema: { - body: { - type: 'object', - required: ['key', 'value'], - properties: { - key: { type: 'string' }, - value: { type: 'string' }, + try { + const port = +(process.env.PORT || 8080); + const host = process.env.HOST || '0.0.0.0'; + + const server = fastify(); + const secretsOptions = { + schema: { + body: { + type: 'object', + required: ['key', 'value'], + properties: { + key: { type: 'string' }, + value: { type: 'string' }, + }, }, }, - }, - }; - secretsServer.post('/secrets', secretsOptions, async (request, reply) => { - try { - const { body: secret } = request; - logger.info(`got request to add new mask: ${JSON.stringify(secret)}`); - this.taskLogger.addNewMask(secret); - reply.code(201); - return 'secret added'; - } catch (err) { - logger.info(`could not create new mask due to error: ${err}`); - reply.code(500); - throw err; - } - }); + }; + server.post('/secrets', secretsOptions, async (request, reply) => { + try { + const { body: secret } = request; + logger.info(`got request to add new mask: ${JSON.stringify(secret)}`); + this.taskLogger.addNewMask(secret); + reply.code(201); + return 'secret added'; + } catch (err) { + logger.info(`could not create new mask due to error: ${err}`); + reply.code(500); + throw err; + } + }); - const address = await secretsServer.listen({ host, port }); - logger.info(`listening for engine updates on ${address}`); + const address = await server.listen({ host, port }); + deferredServerAddress.resolve(address); + logger.info(`listening for engine updates on ${address}`); + } catch (error) { + logger.error(`could not start server for engine updates due to error: ${error}`); + deferredServerAddress.reject(error); + throw error; + } } _handleContainerStreamEnd(containerId) { @@ -392,7 +401,7 @@ class Logger { // do not call before build is finished _awaitAllStreamsClosed() { - const deferred = Q.defer(); + const deferred = getPromiseWithResolvers(); this._checkAllStreamsClosed(deferred); this.finishedContainersEmitter.on('end', this._checkAllStreamsClosed.bind(this, deferred)); return deferred.promise; @@ -423,4 +432,7 @@ class Logger { } } -module.exports = Logger; +module.exports = { + Logger, + secretsServerAddress: deferredServerAddress.promise, +}; diff --git a/package.json b/package.json index 6efb787..582459b 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@codefresh-io/task-logger": "^1.12.3", "cf-errors": "^0.1.16", "cf-logs": "^1.1.25", + "core-js-pure": "^3.37.1", "docker-events": "0.0.2", "dockerode": "^2.5.8", "fastify": "^4.28.1", diff --git a/test/addNewMask.unit.spec.js b/test/addNewMask.unit.spec.js index 87c7d05..b3a42f1 100644 --- a/test/addNewMask.unit.spec.js +++ b/test/addNewMask.unit.spec.js @@ -1,12 +1,11 @@ /* jshint ignore:start */ -'use strict'; -const Q = require('q'); - +const timers = require('node:timers/promises'); const chai = require('chai'); const expect = chai.expect; const sinon = require('sinon'); const sinonChai = require('sinon-chai'); +const { getPromiseWithResolvers } = require('../lib/helpers'); const proxyquire = require('proxyquire').noCallThru(); chai.use(sinonChai); @@ -27,21 +26,24 @@ describe('addNewMask', () => { describe('positive', () => { it('should send a request to add a secret', async () => { - const rpSpy = sinon.spy(() => Q.resolve({ statusCode: 201 })); + const rpSpy = sinon.spy(async () => ({ statusCode: 201 })); + const deferredAddress = getPromiseWithResolvers(); const addNewMask = proxyquire('../lib/addNewMask', { - 'request-promise': rpSpy + 'request-promise': rpSpy, + './logger': { + secretsServerAddress: deferredAddress.promise, + }, }); - - process.env.PORT = 1337; - process.env.HOST = '127.0.0.1'; const secret = { key: '123', value: 'ABC', }; + deferredAddress.resolve('http://127.0.0.1:1337'); addNewMask(secret); + await timers.setTimeout(10); expect(rpSpy).to.have.been.calledOnceWith({ uri: `http://127.0.0.1:1337/secrets`, method: 'POST', @@ -49,30 +51,31 @@ describe('addNewMask', () => { body: secret, resolveWithFullResponse: true, }); - await Q.delay(10); + await timers.setTimeout(10); expect(process.exit).to.have.been.calledOnceWith(0); }); }); describe('negative', () => { it('should send a request to add a secret', async () => { - const rpSpy = sinon.spy(() => Q.reject('could not send request')); + const rpSpy = sinon.spy(async () => { throw 'could not send request';}); + deferredAddress = getPromiseWithResolvers(); const addNewMask = proxyquire('../lib/addNewMask', { - 'request-promise': rpSpy + 'request-promise': rpSpy, + './logger': { + secretsServerAddress: deferredAddress.promise, + }, }); - - process.env.PORT = 1337; - process.env.HOST = '127.0.0.1'; const secret = { key: '123', value: 'ABC', }; + deferredAddress.resolve('http://127.0.0.1:1337'); addNewMask(secret); - await Q.delay(10); + await timers.setTimeout(10); expect(process.exit).to.have.been.calledOnceWith(1); }); }); }); - diff --git a/test/logger.unit.spec.js b/test/logger.unit.spec.js index a272657..8dea4e2 100644 --- a/test/logger.unit.spec.js +++ b/test/logger.unit.spec.js @@ -35,7 +35,7 @@ describe('Logger tests', () => { describe('constructor', () => { it('should define workflow log size limit default in case of non provided value', () => { - const Logger = proxyquire('../lib/logger', {}); + const { Logger } = proxyquire('../lib/logger', {}); const loggerId = 'loggerId'; const firebaseAuthUrl = 'firebaseAuthUrl'; @@ -54,7 +54,7 @@ describe('Logger tests', () => { }); it('should use passed workflow log size limit in case passed', () => { - const Logger = proxyquire('../lib/logger', {}); + const { Logger } = proxyquire('../lib/logger', {}); const loggerId = 'loggerId'; const firebaseAuthUrl = 'firebaseAuthUrl'; @@ -91,7 +91,7 @@ describe('Logger tests', () => { return Q.resolve(taskLogger); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: TaskLoggerFactory }, 'express': expressMock, }); @@ -135,7 +135,7 @@ describe('Logger tests', () => { return Q.resolve(taskLogger); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: TaskLoggerFactory }, 'express': expressMock, }); @@ -177,7 +177,7 @@ describe('Logger tests', () => { return Q.resolve(taskLogger); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: TaskLoggerFactory }, 'express': expressMock, }); @@ -218,7 +218,7 @@ describe('Logger tests', () => { return Q.resolve(taskLogger); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: TaskLoggerFactory }, 'express': expressMock, }); @@ -264,7 +264,7 @@ describe('Logger tests', () => { return Q.reject(new Error('my error')); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: TaskLoggerFactory }, 'express': expressMock, }); @@ -294,7 +294,7 @@ describe('Logger tests', () => { callback(null, [{}, {}]); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'dockerode': function () { return { listContainers: listContainersSpy @@ -317,7 +317,7 @@ describe('Logger tests', () => { callback(null, []); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'dockerode': function () { return { listContainers: listContainersSpy @@ -344,7 +344,7 @@ describe('Logger tests', () => { callback(new Error('getting containers error')); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'dockerode': function () { return { listContainers: listContainersSpy @@ -373,7 +373,7 @@ describe('Logger tests', () => { it('should call handleContainer in case of an create event', () => { const startSpy = sinon.spy(); const onSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'docker-events': function () { return { start: startSpy, @@ -400,7 +400,7 @@ describe('Logger tests', () => { callback(); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'fs': { writeFile: writeFileSpy, readFileSync: sinon.spy(() => { @@ -420,7 +420,7 @@ describe('Logger tests', () => { callback(new Error('write error')); }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'fs': { writeFile: writeFileSpy, readFileSync: sinon.spy(() => { @@ -440,7 +440,7 @@ describe('Logger tests', () => { describe('validate', () => { it('should call process exit in case firebase authentication url was not provided', () => { - const Logger = proxyquire('../lib/logger', {}); + const { Logger } = proxyquire('../lib/logger', {}); const loggerId = 'loggerId'; const firebaseAuthUrl = 'firebaseAuthUrl'; @@ -461,7 +461,7 @@ describe('Logger tests', () => { }); process.exit = processExitSpy; - const Logger = proxyquire('../lib/logger', {}); + const { Logger } = proxyquire('../lib/logger', {}); const loggerId = 'loggerId'; const firebaseAuthUrl = ''; @@ -482,7 +482,7 @@ describe('Logger tests', () => { }); process.exit = processExitSpy; - const Logger = proxyquire('../lib/logger', {}); + const { Logger } = proxyquire('../lib/logger', {}); const loggerId = 'loggerId'; const firebaseAuthUrl = 'firebaseAuthUrl'; @@ -503,7 +503,7 @@ describe('Logger tests', () => { }); process.exit = processExitSpy; - const Logger = proxyquire('../lib/logger', {}); + const { Logger } = proxyquire('../lib/logger', {}); const loggerId = ''; const firebaseAuthUrl = 'firebaseAuthUrl'; @@ -538,7 +538,7 @@ describe('Logger tests', () => { emitter.start = startSpy; return emitter; }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -586,7 +586,7 @@ describe('Logger tests', () => { it('should pass received step log size limit to ContainerLogger', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -633,7 +633,7 @@ describe('Logger tests', () => { it('should pass undefined step log size limit to ContainerLogger in case of no label', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -676,7 +676,7 @@ describe('Logger tests', () => { it('should update total log size when new log message event was sent', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -735,7 +735,7 @@ describe('Logger tests', () => { }); const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -804,7 +804,7 @@ describe('Logger tests', () => { it('was previously handled', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -849,7 +849,7 @@ describe('Logger tests', () => { it('no loggerId', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -889,7 +889,7 @@ describe('Logger tests', () => { it('no containerId', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -922,7 +922,7 @@ describe('Logger tests', () => { it('no container status', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -955,7 +955,7 @@ describe('Logger tests', () => { it('no step name', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -988,7 +988,7 @@ describe('Logger tests', () => { it('no strategy provided', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1022,7 +1022,7 @@ describe('Logger tests', () => { it('provided strategy does not exist', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1057,7 +1057,7 @@ describe('Logger tests', () => { it('container status is create and strategy is logs', () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1103,7 +1103,7 @@ describe('Logger tests', () => { it('should return true in case log limit exceeded', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1151,7 +1151,7 @@ describe('Logger tests', () => { it('should return false in case log limit not exceeded', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1207,7 +1207,7 @@ describe('Logger tests', () => { it('should return false in case log limit was not defined', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1258,7 +1258,7 @@ describe('Logger tests', () => { it('should return true in case log limit exceeded for one container logger', async () => { const infoSpy = sinon.spy(); const errorSpy = sinon.spy(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1309,7 +1309,7 @@ describe('Logger tests', () => { emitter.start = sinon.spy(() => Q.resolve()); return emitter; }); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { 'cf-logs': { Logger: () => { return { @@ -1376,7 +1376,7 @@ describe('Logger tests', () => { const containerLogger = new EventEmitter(); containerLogger.start = () => Q.resolve(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: () => Q.resolve(taskLogger) }, 'docker-events': function () { return dockerEvents; }, './ContainerLogger': function () { return containerLogger; }, @@ -1466,7 +1466,7 @@ describe('Logger tests', () => { const containerLogger = new EventEmitter(); containerLogger.start = () => Q.resolve(); - const Logger = proxyquire('../lib/logger', { + const { Logger } = proxyquire('../lib/logger', { '@codefresh-io/task-logger': { TaskLogger: () => Q.resolve(taskLogger) }, 'docker-events': function () { return dockerEvents; }, './ContainerLogger': function () { return containerLogger; }, @@ -1506,7 +1506,7 @@ describe('Logger tests', () => { // return Q.resolve(taskLogger); // }); - // const Logger = proxyquire('../lib/logger', { + // const { Logger } = proxyquire('../lib/logger', { // '@codefresh-io/task-logger': { TaskLogger: TaskLoggerFactory }, // 'express': expressMock, // }); diff --git a/yarn.lock b/yarn.lock index 0ca9773..bc7e882 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1839,6 +1839,11 @@ core-js-pure@^3.20.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== +core-js-pure@^3.37.1: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.37.1.tgz#2b4b34281f54db06c9a9a5bd60105046900553bd" + integrity sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA== + core-js@^2.4.0, core-js@^2.5.0: version "2.6.11" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"