diff --git a/.jenkins/config.json b/.jenkins/config-mysql.json similarity index 100% rename from .jenkins/config.json rename to .jenkins/config-mysql.json diff --git a/.jenkins/config-sqlite.json b/.jenkins/config-sqlite.json new file mode 100644 index 00000000..c4ead63d --- /dev/null +++ b/.jenkins/config-sqlite.json @@ -0,0 +1,21 @@ +{ + "database": { + "engine": "knex-native", + "knex": { + "client": "sqlite3", + "connection": { + "filename": "/data/database.sqlite" + }, + "pool": { + "min": 0, + "max": 1, + "createTimeoutMillis": 3000, + "acquireTimeoutMillis": 30000, + "idleTimeoutMillis": 30000, + "reapIntervalMillis": 1000, + "createRetryIntervalMillis": 100, + "propagateCreateError": false + } + } + } +} diff --git a/Jenkinsfile b/Jenkinsfile index 1d8a287a..711d7bb3 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -83,11 +83,11 @@ pipeline { ''' } } - stage('Test') { + stage('Integration Tests Sqlite') { steps { // Bring up a stack - sh 'docker-compose up -d fullstack' - sh './scripts/wait-healthy $(docker-compose ps -q fullstack) 120' + sh 'docker-compose up -d fullstack-sqlite' + sh './scripts/wait-healthy $(docker-compose ps -q fullstack-sqlite) 120' // Run tests sh 'rm -rf test/results' @@ -99,8 +99,36 @@ pipeline { always { // Dumps to analyze later sh 'mkdir -p debug' - sh 'docker-compose logs fullstack | gzip > debug/docker_fullstack.log.gz' + sh 'docker-compose logs fullstack-sqlite | gzip > debug/docker_fullstack_sqlite.log.gz' sh 'docker-compose logs db | gzip > debug/docker_db.log.gz' + sh 'docker-compose down' + // Cypress videos and screenshot artifacts + dir(path: 'test/results') { + archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' + } + junit 'test/results/junit/*' + } + } + } + stage('Integration Tests Mysql') { + steps { + // Bring up a stack + sh 'docker-compose up -d fullstack-mysql' + sh './scripts/wait-healthy $(docker-compose ps -q fullstack-mysql) 120' + + // Run tests + sh 'rm -rf test/results' + sh 'docker-compose up cypress' + // Get results + sh 'docker cp -L "$(docker-compose ps -q cypress):/results" test/' + } + post { + always { + // Dumps to analyze later + sh 'mkdir -p debug' + sh 'docker-compose logs fullstack-mysql | gzip > debug/docker_fullstack_mysql.log.gz' + sh 'docker-compose logs db | gzip > debug/docker_db.log.gz' + sh 'docker-compose down' // Cypress videos and screenshot artifacts dir(path: 'test/results') { archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml' diff --git a/backend/setup.js b/backend/setup.js index db618add..e47431f0 100644 --- a/backend/setup.js +++ b/backend/setup.js @@ -8,92 +8,101 @@ const authModel = require('./models/auth'); const settingModel = require('./models/setting'); const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG; -function setupJwt(resolve, reject) { - // Now go and check if the jwt gpg keys have been created and if not, create them - if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) { - logger.info('Creating a new JWT key pair...'); +/** + * Creates a new JWT RSA Keypair if not alread set on the config + * + * @returns {Promise} + */ +const setupJwt = () => { + return new Promise((resolve, reject) => { + // Now go and check if the jwt gpg keys have been created and if not, create them + if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) { + logger.info('Creating a new JWT key pair...'); - // jwt keys are not configured properly - const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json'; - let config_data = {}; + // jwt keys are not configured properly + const filename = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json'; + let config_data = {}; - try { - config_data = require(filename); - } catch (err) { - // do nothing + try { + config_data = require(filename); + } catch (err) { + // do nothing + if (debug_mode) { + logger.debug(filename + ' config file could not be required'); + } + } + + // Now create the keys and save them in the config. + let key = new NodeRSA({ b: 2048 }); + key.generateKeyPair(); + + config_data.jwt = { + key: key.exportKey('private').toString(), + pub: key.exportKey('public').toString(), + }; + + // Write config + fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => { + if (err) { + logger.error('Could not write JWT key pair to config file: ' + filename); + reject(err); + } else { + logger.info('Wrote JWT key pair to config file: ' + filename); + + logger.warn('Restarting interface to apply new configuration'); + process.exit(0); + } + }); + } else { + // JWT key pair exists if (debug_mode) { - logger.debug(filename + ' config file could not be required'); + logger.debug('JWT Keypair already exists'); } + + resolve(); } + }); +}; - // Now create the keys and save them in the config. - let key = new NodeRSA({b: 2048}); - key.generateKeyPair(); - - config_data.jwt = { - key: key.exportKey('private').toString(), - pub: key.exportKey('public').toString() - }; - - // Write config - fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => { - if (err) { - logger.error('Could not write JWT key pair to config file: ' + filename); - reject(err); - } else { - logger.info('Wrote JWT key pair to config file: ' + filename); - - logger.warn('Restarting interface to apply new configuration'); - process.exit(0); - } - }); - - } else { - // JWT key pair exists - if (debug_mode) { - logger.debug('JWT Keypair already exists'); - } - - resolve(); - } -} - -function setupDefaultUser() { - (userModel +/** + * Creates a default admin users if one doesn't already exist in the database + * + * @returns {Promise} + */ +const setupDefaultUser = () => { + return userModel .query() .select(userModel.raw('COUNT(`id`) as `count`')) .where('is_deleted', 0) .first() - ).then( (row) => { - if (!row.count) { - // Create a new user and set password - logger.info('Creating a new user: admin@example.com with password: changeme'); + .then((row) => { + if (!row.count) { + // Create a new user and set password + logger.info('Creating a new user: admin@example.com with password: changeme'); - let data = { - is_deleted: 0, - email: 'admin@example.com', - name: 'Administrator', - nickname: 'Admin', - avatar: '', - roles: ['admin'] - }; + let data = { + is_deleted: 0, + email: 'admin@example.com', + name: 'Administrator', + nickname: 'Admin', + avatar: '', + roles: ['admin'], + }; - return userModel - .query() - .insertAndFetch(data) - .then( (user) => { - return authModel - .query() - .insert({ - user_id: user.id, - type: 'password', - secret: 'changeme', - meta: {} - }) - .then(() => { - return userPermissionModel - .query() - .insert({ + return userModel + .query() + .insertAndFetch(data) + .then((user) => { + return authModel + .query() + .insert({ + user_id: user.id, + type: 'password', + secret: 'changeme', + meta: {}, + }) + .then(() => { + return userPermissionModel.query().insert({ user_id: user.id, visibility: 'all', proxy_hosts: 'manage', @@ -101,25 +110,31 @@ function setupDefaultUser() { dead_hosts: 'manage', streams: 'manage', access_lists: 'manage', - certificates: 'manage' + certificates: 'manage', }); - }); - }) - .then(() => { - logger.info('Initial admin setup completed'); - }); - } else if (debug_mode) { - logger.debug('Admin user setup not required'); - } - }); -} + }); + }) + .then(() => { + logger.info('Initial admin setup completed'); + }); + } else if (debug_mode) { + logger.debug('Admin user setup not required'); + } + }); +}; -function setupDefaultSettings() { +/** + * Creates default settings if they don't already exist in the database + * + * @returns {Promise} + */ +const setupDefaultSettings = () => { return settingModel .query() - .select(userModel.raw('COUNT(`id`) as `count`')) + .select(settingModel.raw('COUNT(`id`) as `count`')) + .where({id: 'default-site'}) .first() - .then( (row) => { + .then((row) => { if (!row.count) { settingModel .query() @@ -128,22 +143,20 @@ function setupDefaultSettings() { name: 'Default Site', description: 'What to show when Nginx is hit with an unknown Host', value: 'congratulations', - meta: {} - }).then(() => { + meta: {}, + }) + .then(() => { logger.info('Default settings added'); }); - } if (debug_mode) { + } + if (debug_mode) { logger.debug('Default setting setup not required'); } }); -} +}; module.exports = function () { - return new Promise((resolve, reject) => { - return setupJwt(resolve, reject); - }).then(() => { - return setupDefaultUser(); - }).then(() => { - return setupDefaultSettings(); - }); + return setupJwt() + .then(setupDefaultUser) + .then(setupDefaultSettings); }; diff --git a/docker/docker-compose.ci.yml b/docker/docker-compose.ci.yml index dbc122c7..7050b17e 100644 --- a/docker/docker-compose.ci.yml +++ b/docker/docker-compose.ci.yml @@ -2,14 +2,14 @@ version: "3" services: - fullstack: + fullstack-mysql: image: ${IMAGE}:ci-${BUILD_NUMBER} environment: - NODE_ENV=development - FORCE_COLOR=1 volumes: - npm_data:/data - - ../.jenkins/config.json:/app/config/production.json + - ../.jenkins/config-mysql.json:/app/config/production.json expose: - 81 - 80 @@ -17,6 +17,19 @@ services: depends_on: - db + fullstack-sqlite: + image: ${IMAGE}:ci-${BUILD_NUMBER} + environment: + - NODE_ENV=development + - FORCE_COLOR=1 + volumes: + - npm_data:/data + - ../.jenkins/config-sqlite.json:/app/config/production.json + expose: + - 81 + - 80 + - 443 + db: image: jc21/mariadb-aria environment: