Skip to content

Commit

Permalink
Merge pull request #352 from telefonicaid/task/upgrade_mongoose_8
Browse files Browse the repository at this point in the history
Task/upgrade mongoose 8
  • Loading branch information
GregorioBlazquez authored Dec 16, 2024
2 parents 45ffc3b + 4122afb commit c0d53fc
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 188 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ jobs:
strategy:
matrix:
node-version:
- 14.x
- 16.x
- 18.x
steps:
Expand Down
3 changes: 2 additions & 1 deletion CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
- Deprecated: /iot/services API routes
- Upgrade body-parser dep from 1.19.0 to 1.20.3
- Upgrade express from 4.19.2 to 4.20.0
- Upgrade mongoose dep from 5.13.20 to 5.13.22
- Upgrade mongodb devdep from 4.17.0 to 4.17.2
- Upgrade mongoose dep from 5.13.20 to 8.4.4 (solving vulnerability CVE-2024-53900) (#351)
4 changes: 2 additions & 2 deletions lib/model/Configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ const Configuration = new Schema({
transport: String
});

function load(db) {
function load() {
Configuration.index({ apikey: 1, resource: 1, protocol: 1 }, { unique: true });
module.exports.model = db.model('Configuration', Configuration);
module.exports.model = mongoose.model('Configuration', Configuration);
module.exports.internalSchema = Configuration;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/model/Protocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const Protocol = new Schema({
description: String
});

function load(db) {
function load() {
Protocol.index({ protocol: 1, resource: 1 }, { unique: true });
module.exports.model = db.model('Protocol', Protocol);
module.exports.model = mongoose.model('Protocol', Protocol);
module.exports.internalSchema = Protocol;
}

Expand Down
178 changes: 73 additions & 105 deletions lib/model/dbConn.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@
const mongoose = require('mongoose');
const config = require('../utils/commonConfig');
const constants = require('../utils/constants');
const async = require('async');
const iotagentLib = require('iotagent-node-lib');
const errors = require('../errors');
let defaultDb;
const DEFAULT_DB_NAME = 'iotagent-manager';
let defaultDb;

mongoose.Promise = global.Promise; // not including this causes DeprecationWarning

function loadModels() {
require('./Protocol').load(defaultDb);
require('./Configuration').load(defaultDb);
require('./Protocol').load();
require('./Configuration').load();
}

/**
Expand All @@ -48,76 +48,44 @@ function loadModels() {
*/
function init(logger, host, db, port, username, password, options, callback) {
let url;
let credentials = '';
let retries = 0;
let lastError;
const maxRetries =
(config.getConfig().mongodb && config.getConfig().mongodb.retries) || constants.DEFAULT_MONGODB_RETRIES;

if (username && password) {
credentials = username + ':' + password + '@';
}
const maxRetries = config.getConfig().mongodb?.retries || constants.DEFAULT_MONGODB_RETRIES;

function addPort(item) {
return item + ':' + port;
return `${item}:${port}`;
}

function commaConcat(previous, current, currentIndex) {
if (currentIndex !== 0) {
previous += ',';
}

previous += current;
url = 'mongodb://';

return previous;
if (options.auth) {
url += `${encodeURIComponent(options.auth.user)}:${encodeURIComponent(options.auth.password)}@`;
}

const hosts = host.split(',').map(addPort).reduce(commaConcat, '');
const hosts = host.split(',').map(addPort).join(',');
url += `${hosts}/${db}`;

url = 'mongodb://' + credentials + hosts + '/' + db;

if (options.replicaSet) {
url += '?replicaSet=' + options.replicaSet.rs_name;
}

/* eslint-disable-next-line no-unused-vars */
function createConnectionHandler(error, results) {
if (defaultDb) {
logger.info('Successfully connected to MongoDB.');
module.exports.db = defaultDb;
loadModels();
} else {
logger.error('MONGODB-002: Error found after [%d] attempts: %s', retries, error || lastError);
if (options.extraArgs) {
const query = new URLSearchParams(options.extraArgs).toString();
if (query) {
url += `?${query}`;
}

callback(error);
}

function retryCheck() {
return !defaultDb && retries < maxRetries;
delete options.extraArgs;
}

function connectionAttempt(url, options, callback) {
logger.info(
'Attempting to connect to MongoDB instance with url %j and options %j. Attempt %d',
url,
options,
retries
);
// just to avoid warnings with recent mongoose versions
options.useNewUrlParser = true;
options.useUnifiedTopology = true;
mongoose.set('useCreateIndex', true);
/* eslint-disable-next-line no-unused-vars */
const candidateDb = mongoose.createConnection(url, options, function (error, result) {
if (error) {
logger.error('MONGODB-001: Error trying to connect to MongoDB: %s', error);
lastError = error;
} else {
defaultDb = candidateDb;
function connectionAttempt(callback) {
logger.info(`Attempting to connect to MongoDB at ${url}. Attempt ${retries + 1}`);

mongoose
.connect(url, options)
.then(() => {
defaultDb = mongoose.connection;
logger.info('Successfully connected to MongoDB.');
loadModels();
defaultDb.on('error', function (error) {
logger.error('Mongo Driver error: %j', error);
lastError = error;
iotagentLib.alarms.raise(iotagentLib.constants.MONGO_ALARM, error);
});
/* eslint-disable-next-line no-unused-vars */
defaultDb.on('connecting', function (error) {
Expand Down Expand Up @@ -145,63 +113,63 @@ function init(logger, host, db, port, username, password, options, callback) {
defaultDb.on('close', function () {
logger.debug('Mongo Driver close');
});
}

callback();
});
}

function tryCreateConnection(callback) {
const attempt = async.apply(connectionAttempt, url, options, callback);
const seconds =
(config.getConfig().mongodb && config.getConfig().mongodb.retryTime) ||
constants.DEFAULT_MONGODB_RETRY_TIME;

retries++;

if (retries === 1) {
logger.info('First connection attempt');
attempt();
} else {
logger.info('Waiting %d seconds before attempting again.', seconds);
setTimeout(attempt, seconds * 1000);
}
callback();
})
.catch((err) => {
logger.error(`MONGODB-001: Error trying to connect to MongoDB: ${err}`);
lastError = err;
retries++;
if (retries < maxRetries) {
const retryTime = config.getConfig().mongodb?.retryTime || constants.DEFAULT_MONGODB_RETRY_TIME;
logger.info(`Retrying in ${retryTime} seconds...`);
setTimeout(() => connectionAttempt(callback), retryTime * 1000);
} else {
logger.error('MONGODB-002: Error to connect found after %d attempts: %s', retries, lastError);
callback(err);
}
});
}

defaultDb = null;
async.whilst(retryCheck, tryCreateConnection, createConnectionHandler);
connectionAttempt(callback);
}

function configureDb(logger, callback) {
const currentConfig = config.getConfig();

if (!currentConfig.mongodb || !currentConfig.mongodb.host) {
if (!currentConfig.mongodb?.host) {
logger.fatal('No host found for MongoDB driver.');
callback(new errors.BadConfiguration('No host found for MongoDB driver'));
} else {
let dbName = currentConfig.mongodb.db;
const port = currentConfig.mongodb.port || 27017;
const options = {};

if (!currentConfig.mongodb.db) {
dbName = DEFAULT_DB_NAME;
}
throw new errors.BadConfiguration('No host found for MongoDB driver');
}

if (currentConfig.mongodb.replicaSet) {
options.replset = { rs_name: currentConfig.mongodb.replicaSet };
const dbName = currentConfig.mongodb.db || DEFAULT_DB_NAME;
const port = currentConfig.mongodb.port || 27017;
const options = {};

if (currentConfig.mongodb.replicaSet) options.replicaSet = currentConfig.mongodb.replicaSet;
if (currentConfig.mongodb.ssl) options.ssl = currentConfig.mongodb.ssl;
if (currentConfig.mongodb.extraArgs) options.extraArgs = currentConfig.mongodb.extraArgs;
if (currentConfig.mongodb.user && currentConfig.mongodb.password) {
options.auth = {
user: currentConfig.mongodb.user,
password: currentConfig.mongodb.password
};
if (currentConfig.mongodb.authSource) {
options.extraArgs = {
...options.extraArgs,
authSource: currentConfig.mongodb.authSource
};
}

init(
logger,
config.getConfig().mongodb.host,
dbName,
port,
currentConfig.username,
currentConfig.password,
options,
callback
);
}

init(
logger,
currentConfig.mongodb.host,
dbName,
port,
currentConfig.username,
currentConfig.password,
options,
callback
);
}

exports.configureDb = configureDb;
Expand Down
57 changes: 41 additions & 16 deletions lib/services/configurationData.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function createGetWithFields(fields) {
logger.debug('Looking for configuration with params %j and query %j', fields, queryObj);

const query = Configuration.model.find(queryObj);

const queryCount = Configuration.model.countDocuments(queryObj);
if (limit) {
query.limit(parseInt(limit, 10));
}
Expand All @@ -79,21 +79,39 @@ function createGetWithFields(fields) {
query.skip(parseInt(offset, 10));
}

async.series(
[query.exec.bind(query), Configuration.model.countDocuments.bind(Configuration.model, queryObj)],
function (error, results) {
if (!error && results && results.length === 2) {
callback(null, {
services: results[0],
count: results[1]
});
} else if (error) {
callback(new errors.InternalDbError(error));
} else {
callback(new errors.DeviceGroupNotFound(fields, arguments));
}
function funcQuery(cb) {
query
.exec({})
.then((res) => {
cb(null, res);
})
.catch((error) => {
cb(error);
});
}
function funcQueryCount(cb) {
queryCount
.exec({})
.then((res) => {
cb(null, res);
})
.catch((error) => {
cb(error);
});
}

async.series([funcQuery, funcQueryCount], function (error, results) {
if (!error && results && results.length === 2) {
callback(null, {
services: results[0],
count: results[1]
});
} else if (error) {
callback(new errors.InternalDbError(error));
} else {
callback(new errors.DeviceGroupNotFound(fields, arguments));
}
);
});
};
}

Expand Down Expand Up @@ -143,7 +161,14 @@ function save(theLogger, protocol, description, iotagent, resource, configuratio
}
}
theLogger.debug('Saving Configuration %j translated to %j ', configuration, configurationObj);
configurationObj.save(callback);
configurationObj
.save({})
.then((res) => {
callback(null, res);
})
.catch((error) => {
callback(error);
});
}

exports.get = iotagentLib.alarms.intercept(
Expand Down
Loading

0 comments on commit c0d53fc

Please sign in to comment.