preparing for fully revertable config deployment: executeConfig() (ref. #48)

merge-requests/23/merge
Michał 'rysiek' Woźniak 2024-01-29 23:22:15 +00:00
rodzic b159976478
commit 4bf42c1c93
2 zmienionych plików z 119 dodań i 113 usunięć

Wyświetl plik

@ -337,15 +337,6 @@ describe('service-worker', async () => {
window.test_id = 0
it("should set-up LibResilientPlugins", async () => {
// we cannot import the same module multiple times:
// https://github.com/denoland/deno/issues/6946
//
// ...so we have to use a query-param hack, sigh
await import("../../service-worker.js?" + window.test_id);
assert(self.LibResilientPlugins instanceof Array)
})
it("should use default LibResilientConfig values when config.json is missing", async () => {
let mock_response_data = {

Wyświetl plik

@ -2,11 +2,6 @@
* LibResilient Service Worker.
*/
// initialize the LibResilientPlugins array
if (!Array.isArray(self.LibResilientPlugins)) {
self.LibResilientPlugins = new Array()
}
// initialize the LibResilientConfig array
//
// this also sets some sane defaults,
@ -277,6 +272,118 @@ let getConfigJSON = async (cresponse) => {
}
/**
* execute on the configuration
*
* load plugin modules, making constructors available
* cycle through the plugin config instantiating plugins and their dependencies
*/
let executeConfig = (pluginsConfig) => {
// clean version of LibResilientPlugins
// NOTICE: this assumes LibResilientPlugins is not ever used *befure* this runs
// NOTICE: this assumption seems to hold currently, but noting for clarity
self.LibResilientPlugins = new Array()
// this is the stash for plugins that need dependencies instantiated first
let dependentPlugins = new Array()
// only now load the plugins (config.json could have changed the defaults)
while (pluginsConfig.length > 0) {
// get the first plugin config from the array
let pluginConfig = pluginsConfig.shift()
self.log('service-worker', `handling plugin type: ${pluginConfig.name}`)
// load the relevant plugin script (if not yet loaded)
if (!LibResilientPluginConstructors.has(pluginConfig.name)) {
self.log('service-worker', `${pluginConfig.name}: loading plugin's source`)
self.importScripts(`./plugins/${pluginConfig.name}/index.js`)
}
// do we have any dependencies we should handle first?
if (typeof pluginConfig.uses !== "undefined") {
self.log('service-worker', `${pluginConfig.name}: ${pluginConfig.uses.length} dependencies found`)
// move the dependency plugin configs to LibResilientConfig to be worked on next
for (let i=(pluginConfig.uses.length); i--; i>=0) {
self.log('service-worker', `${pluginConfig.name}: dependency found: ${pluginConfig.uses[i].name}`)
// put the plugin config in front of the plugin configs array
pluginsConfig.unshift(pluginConfig.uses[i])
// set each dependency plugin config to false so that we can keep track
// as we fill those gaps later with instantiated dependency plugins
pluginConfig.uses[i] = false
}
// stash the plugin config until we have all the dependencies handled
self.log('service-worker', `${pluginConfig.name}: not instantiating until dependencies are ready`)
dependentPlugins.push(pluginConfig)
// move on to the next plugin config, which at this point will be
// the first of dependencies for the plugin whose config got stashed
continue;
}
do {
// if the plugin is not enabled, no instantiation for it nor for its dependencies
// if the pluginConfig does not have an "enabled" field, it should be assumed to be "true"
if ( ( "enabled" in pluginConfig ) && ( pluginConfig.enabled != true ) ) {
self.log('service-worker', `skipping ${pluginConfig.name} instantiation: plugin not enabled (dependencies will also not be instantiated)`)
pluginConfig = dependentPlugins.pop()
if (pluginConfig !== undefined) {
let didx = pluginConfig.uses.indexOf(false)
pluginConfig.uses.splice(didx, 1)
}
continue;
}
// instantiate the plugin
let plugin = LibResilientPluginConstructors.get(pluginConfig.name)(self, pluginConfig)
self.log('service-worker', `${pluginConfig.name}: instantiated`)
// do we have a stashed plugin that requires dependencies?
if (dependentPlugins.length === 0) {
// no we don't; so, this plugin goes directly to the plugin list
self.LibResilientPlugins.push(plugin)
// we're done here
self.log('service-worker', `${pluginConfig.name}: no dependent plugins, pushing directly to LibResilientPlugins`)
break;
}
// at this point clearly there is at least one element in dependentPlugins
// so we can safely assume that the freshly instantiated plugin is a dependency
//
// in that case let's find the first empty spot for a dependency
let didx = dependentPlugins[dependentPlugins.length - 1].uses.indexOf(false)
// assign the freshly instantiated plugin as that dependency
dependentPlugins[dependentPlugins.length - 1].uses[didx] = plugin
self.log('service-worker', `${pluginConfig.name}: assigning as dependency (#${didx}) to ${dependentPlugins[dependentPlugins.length - 1].name}`)
// was this the last one?
if (didx >= dependentPlugins[dependentPlugins.length - 1].uses.length - 1) {
// yup, last one!
self.log('service-worker', `${pluginConfig.name}: this was the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
// we can now proceed to instantiate the last element of dependentPlugins
pluginConfig = dependentPlugins.pop()
continue
}
// it is not the last one, so there should be more dependency plugins to instantiate first
// before we can instantiate the last of element of dependentPlugins
// but that requires the full treatment, including checing the `uses` field for their configs
self.log('service-worker', `${pluginConfig.name}: not yet the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
pluginConfig = false
// if pluginConfig is not false, rinse-repeat the plugin instantiation steps
// since we are dealing with the last element of dependentPlugins
} while ( (pluginConfig !== false) && (pluginConfig !== undefined) )
}
}
// flag signifying the SW has been initialized already
var initDone = false
@ -363,106 +470,14 @@ let initServiceWorker = async () => {
// TODO: find a better way
self.LibResilientPluginConstructors = self.LibResilientPluginConstructors || new Map()
// copy of the plugins config
// we need to work on it so that self.LibResilientConfig.plugins remains unmodified
// point backup of LibResilientPluginConstructors, in case we need to roll back
// TODO: handle in a more elegant way
let lrpcBackup = new Map(self.LibResilientPluginConstructors)
// working on a copy of the plugins config so that
// self.LibResilientConfig.plugins remains unmodified
// in case we need it later (for example, when re-loading the config)
var pluginsConfig = [...self.LibResilientConfig.plugins]
// this is the stash for plugins that need dependencies instantiated first
var dependentPlugins = new Array()
// only now load the plugins (config.json could have changed the defaults)
while (pluginsConfig.length > 0) {
// get the first plugin config from the array
let pluginConfig = pluginsConfig.shift()
self.log('service-worker', `handling plugin type: ${pluginConfig.name}`)
// load the relevant plugin script (if not yet loaded)
if (!LibResilientPluginConstructors.has(pluginConfig.name)) {
self.log('service-worker', `${pluginConfig.name}: loading plugin's source`)
self.importScripts(`./plugins/${pluginConfig.name}/index.js`)
}
// do we have any dependencies we should handle first?
if (typeof pluginConfig.uses !== "undefined") {
self.log('service-worker', `${pluginConfig.name}: ${pluginConfig.uses.length} dependencies found`)
// move the dependency plugin configs to LibResilientConfig to be worked on next
for (var i=(pluginConfig.uses.length); i--; i>=0) {
self.log('service-worker', `${pluginConfig.name}: dependency found: ${pluginConfig.uses[i].name}`)
// put the plugin config in front of the plugin configs array
pluginsConfig.unshift(pluginConfig.uses[i])
// set each dependency plugin config to false so that we can keep track
// as we fill those gaps later with instantiated dependency plugins
pluginConfig.uses[i] = false
}
// stash the plugin config until we have all the dependencies handled
self.log('service-worker', `${pluginConfig.name}: not instantiating until dependencies are ready`)
dependentPlugins.push(pluginConfig)
// move on to the next plugin config, which at this point will be
// the first of dependencies for the plugin whose config got stashed
continue;
}
do {
// if the plugin is not enabled, no instantiation for it nor for its dependencies
// if the pluginConfig does not have an "enabled" field, it should be assumed to be "true"
if ( ( "enabled" in pluginConfig ) && ( pluginConfig.enabled != true ) ) {
self.log('service-worker', `skipping ${pluginConfig.name} instantiation: plugin not enabled (dependencies will also not be instantiated)`)
pluginConfig = dependentPlugins.pop()
if (pluginConfig !== undefined) {
let didx = pluginConfig.uses.indexOf(false)
pluginConfig.uses.splice(didx, 1)
}
continue;
}
// instantiate the plugin
let plugin = LibResilientPluginConstructors.get(pluginConfig.name)(self, pluginConfig)
self.log('service-worker', `${pluginConfig.name}: instantiated`)
// do we have a stashed plugin that requires dependencies?
if (dependentPlugins.length === 0) {
// no we don't; so, this plugin goes directly to the plugin list
self.LibResilientPlugins.push(plugin)
// we're done here
self.log('service-worker', `${pluginConfig.name}: no dependent plugins, pushing directly to LibResilientPlugins`)
break;
}
// at this point clearly there is at least one element in dependentPlugins
// so we can safely assume that the freshly instantiated plugin is a dependency
//
// in that case let's find the first empty spot for a dependency
let didx = dependentPlugins[dependentPlugins.length - 1].uses.indexOf(false)
// assign the freshly instantiated plugin as that dependency
dependentPlugins[dependentPlugins.length - 1].uses[didx] = plugin
self.log('service-worker', `${pluginConfig.name}: assigning as dependency (#${didx}) to ${dependentPlugins[dependentPlugins.length - 1].name}`)
// was this the last one?
if (didx >= dependentPlugins[dependentPlugins.length - 1].uses.length - 1) {
// yup, last one!
self.log('service-worker', `${pluginConfig.name}: this was the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
// we can now proceed to instantiate the last element of dependentPlugins
pluginConfig = dependentPlugins.pop()
continue
}
// it is not the last one, so there should be more dependency plugins to instantiate first
// before we can instantiate the last of element of dependentPlugins
// but that requires the full treatment, including checing the `uses` field for their configs
self.log('service-worker', `${pluginConfig.name}: not yet the last dependency of ${dependentPlugins[dependentPlugins.length - 1].name}`)
pluginConfig = false
// if pluginConfig is not false, rinse-repeat the plugin instantiation steps
// since we are dealing with the last element of dependentPlugins
} while ( (pluginConfig !== false) && (pluginConfig !== undefined) )
}
executeConfig([...self.LibResilientConfig.plugins])
// inform
self.log('service-worker', `service worker initialized.\nstrategy in use: ${self.LibResilientPlugins.map(p=>p.name).join(', ')}`)