Merge pull request #1052 from chaotixkilla/fix-config-updates-requiring-restart

Fix config updates not propagating to machine without restart
This commit is contained in:
Rafael Taranto 2022-01-20 17:34:30 +00:00 committed by GitHub
commit e507807896
5 changed files with 70 additions and 54 deletions

View file

@ -151,9 +151,8 @@ function unpair (rec) {
} }
function reboot (rec) { function reboot (rec) {
return db.none('NOTIFY $1:name, $2', ['poller', JSON.stringify( return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify(
{ {
type: 'machineAction',
action: 'reboot', action: 'reboot',
value: _.pick(['deviceId', 'operatorId', 'action'], rec) value: _.pick(['deviceId', 'operatorId', 'action'], rec)
} }
@ -161,9 +160,8 @@ function reboot (rec) {
} }
function shutdown (rec) { function shutdown (rec) {
return db.none('NOTIFY $1:name, $2', ['poller', JSON.stringify( return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify(
{ {
type: 'machineAction',
action: 'shutdown', action: 'shutdown',
value: _.pick(['deviceId', 'operatorId', 'action'], rec) value: _.pick(['deviceId', 'operatorId', 'action'], rec)
} }
@ -171,9 +169,8 @@ function shutdown (rec) {
} }
function restartServices (rec) { function restartServices (rec) {
return db.none('NOTIFY $1:name, $2', ['poller', JSON.stringify( return db.none('NOTIFY $1:name, $2', ['machineAction', JSON.stringify(
{ {
type: 'machineAction',
action: 'restartServices', action: 'restartServices',
value: _.pick(['deviceId', 'operatorId', 'action'], rec) value: _.pick(['deviceId', 'operatorId', 'action'], rec)
} }

View file

@ -1,11 +1,56 @@
const _ = require('lodash/fp')
const db = require('../db')
const state = require('./state') const state = require('./state')
const newSettingsLoader = require('../new-settings-loader') const newSettingsLoader = require('../new-settings-loader')
const helpers = require('../route-helpers') const helpers = require('../route-helpers')
const logger = require('../logger') const logger = require('../logger')
const { settingsCache } = state db.connect({ direct: true }).then(sco => {
sco.client.on('notification', data => {
const parsedData = JSON.parse(data.payload)
return reload(parsedData.operatorId)
})
return sco.none('LISTEN $1:name', 'reload')
}).catch(console.error)
db.connect({ direct: true }).then(sco => {
sco.client.on('notification', data => {
const parsedData = JSON.parse(data.payload)
return machineAction(parsedData.action, parsedData.value)
})
return sco.none('LISTEN $1:name', 'machineAction')
}).catch(console.error)
function machineAction (type, value) {
const deviceId = value.deviceId
const operatorId = value.operatorId
const pid = state.pids?.[operatorId]?.[deviceId]?.pid
switch (type) {
case 'reboot':
logger.debug(`Rebooting machine '${deviceId}' from operator ${operatorId}`)
state.reboots[operatorId] = { [deviceId]: pid }
break
case 'shutdown':
logger.debug(`Shutting down machine '${deviceId}' from operator ${operatorId}`)
state.shutdowns[operatorId] = { [deviceId]: pid }
break
case 'restartServices':
logger.debug(`Restarting services of machine '${deviceId}' from operator ${operatorId}`)
state.restartServicesMap[operatorId] = { [deviceId]: pid }
break
default:
break
}
}
function reload (operatorId) {
state.needsSettingsReload[operatorId.operatorId] = true
}
const populateSettings = function (req, res, next) { const populateSettings = function (req, res, next) {
const { needsSettingsReload, settingsCache } = state
const operatorId = res.locals.operatorId const operatorId = res.locals.operatorId
const versionId = req.headers['config-version'] const versionId = req.headers['config-version']
if (versionId !== state.oldVersionId) { if (versionId !== state.oldVersionId) {
@ -14,20 +59,21 @@ const populateSettings = function (req, res, next) {
try { try {
const operatorSettings = settingsCache.get(operatorId) const operatorSettings = settingsCache.get(operatorId)
if (!versionId && operatorSettings) { if (!versionId && (!operatorSettings || !!needsSettingsReload[operatorId])) {
req.settings = operatorSettings
return next()
}
if (!versionId && !operatorSettings) {
return newSettingsLoader.loadLatest() return newSettingsLoader.loadLatest()
.then(settings => { .then(settings => {
settingsCache.set(operatorId, settings) settingsCache.set(operatorId, settings)
delete needsSettingsReload[operatorId]
req.settings = settings req.settings = settings
}) })
.then(() => next()) .then(() => next())
.catch(next) .catch(next)
} }
if (!versionId && operatorSettings) {
req.settings = operatorSettings
return next()
}
} catch (e) { } catch (e) {
logger.error(e) logger.error(e)
} }

View file

@ -4,6 +4,7 @@ const SETTINGS_CACHE_REFRESH = 3600
module.exports = (function () { module.exports = (function () {
return { return {
oldVersionId: 'unset', oldVersionId: 'unset',
needsSettingsReload: {},
settingsCache: new NodeCache({ settingsCache: new NodeCache({
stdTTL: SETTINGS_CACHE_REFRESH, stdTTL: SETTINGS_CACHE_REFRESH,
checkperiod: SETTINGS_CACHE_REFRESH // Clear cache every hour checkperiod: SETTINGS_CACHE_REFRESH // Clear cache every hour

View file

@ -2,6 +2,7 @@ const _ = require('lodash/fp')
const db = require('./db') const db = require('./db')
const migration = require('./config-migration') const migration = require('./config-migration')
const { asyncLocalStorage } = require('./async-storage') const { asyncLocalStorage } = require('./async-storage')
const { getOperatorId } = require('./operator')
const OLD_SETTINGS_LOADER_SCHEMA_VERSION = 1 const OLD_SETTINGS_LOADER_SCHEMA_VERSION = 1
const NEW_SETTINGS_LOADER_SCHEMA_VERSION = 2 const NEW_SETTINGS_LOADER_SCHEMA_VERSION = 2
@ -71,12 +72,12 @@ function showAccounts (schemaVersion) {
const configSql = 'insert into user_config (type, data, valid, schema_version) values ($1, $2, $3, $4)' const configSql = 'insert into user_config (type, data, valid, schema_version) values ($1, $2, $3, $4)'
function saveConfig (config) { function saveConfig (config) {
return loadLatestConfigOrNone() return Promise.all([loadLatestConfigOrNone(), getOperatorId('middleware')])
.then(currentConfig => { .then(([currentConfig, operatorId]) => {
const newConfig = _.assign(currentConfig, config) const newConfig = _.assign(currentConfig, config)
return db.tx(t => { return db.tx(t => {
return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION]) return t.none(configSql, ['config', { config: newConfig }, true, NEW_SETTINGS_LOADER_SCHEMA_VERSION])
.then(() => t.none('NOTIFY $1:name, $2', ['poller', JSON.stringify({ type: 'reload', schema: asyncLocalStorage.getStore().get('schema') })])) .then(() => t.none('NOTIFY $1:name, $2', ['reload', JSON.stringify({ schema: asyncLocalStorage.getStore().get('schema'), operatorId })]))
}).catch(console.error) }).catch(console.error)
}) })
} }

View file

@ -81,16 +81,9 @@ cachedVariables.on('expired', (key, val) => {
db.connect({ direct: true }).then(sco => { db.connect({ direct: true }).then(sco => {
sco.client.on('notification', data => { sco.client.on('notification', data => {
const parsedData = JSON.parse(data.payload) const parsedData = JSON.parse(data.payload)
switch (parsedData.type) { return reload(parsedData.schema)
case 'reload':
return reload(parsedData.schema)
case 'machineAction':
return machineAction(parsedData.action, parsedData.value)
default:
break
}
}) })
return sco.none('LISTEN $1:name', 'poller') return sco.none('LISTEN $1:name', 'reload')
}).catch(console.error) }).catch(console.error)
function reload (schema) { function reload (schema) {
@ -98,38 +91,16 @@ function reload (schema) {
store.set('schema', schema) store.set('schema', schema)
// set asyncLocalStorage so settingsLoader loads settings for the right schema // set asyncLocalStorage so settingsLoader loads settings for the right schema
return asyncLocalStorage.run(store, () => { return asyncLocalStorage.run(store, () => {
return settingsLoader.loadLatest().then(settings => { return settingsLoader.loadLatest()
const pi = plugins(settings) .then(settings => {
cachedVariables.set(schema, { settings, pi, isReloading: false }) const pi = plugins(settings)
logger.debug(`Settings for schema '${schema}' reloaded in poller`) cachedVariables.set(schema, { settings, pi, isReloading: false })
return updateAndLoadSanctions() logger.debug(`Settings for schema '${schema}' reloaded in poller`)
}) return updateAndLoadSanctions()
})
}) })
} }
function machineAction (type, value) {
const deviceId = value.deviceId
const operatorId = value.operatorId
const pid = state.pids?.[operatorId]?.[deviceId]?.pid
switch (type) {
case 'reboot':
logger.debug(`Rebooting machine '${deviceId}' from operator ${operatorId}`)
state.reboots[operatorId] = { [deviceId]: pid }
break
case 'shutdown':
logger.debug(`Shutting down machine '${deviceId}' from operator ${operatorId}`)
state.shutdowns[operatorId] = { [deviceId]: pid }
break
case 'restartServices':
logger.debug(`Restarting services of machine '${deviceId}' from operator ${operatorId}`)
state.restartServicesMap[operatorId] = { [deviceId]: pid }
break
default:
break
}
}
function pi () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).pi } function pi () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).pi }
function settings () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).settings } function settings () { return cachedVariables.get(asyncLocalStorage.getStore().get('schema')).settings }