rewriting inheritance logic. splitting default and proto

This commit is contained in:
Axel Kittenberger 2018-06-14 09:18:29 +02:00
parent 6f1f873c29
commit ade3d30c58
4 changed files with 372 additions and 79 deletions

300
default/proto.lua Normal file
View File

@ -0,0 +1,300 @@
--============================================================================
-- proto.lua Live (Mirror) Syncing Demon
--
-- The default behavior of all syncs.
--
-- This default layer 1 functions provide the higher layer functionality.
--
-- License: GPLv2 (see COPYING) or any later version
-- Authors: Axel Kittenberger <axkibe@gmail.com>
--============================================================================
if not default then error( 'default not loaded' ) end
if default.proto then error( 'default-proto already loaded' ) end
proto = { }
default.proto = proto
--
-- used to ensure there aren't typos in the keys
--
proto.checkgauge =
{
action = true,
checkgauge = true,
collect = true,
delay = true,
exitcodes = true,
init = true,
maxDelays = true,
maxProcesses = true,
onAttrib = true,
onCreate = true,
onModify = true,
onDelete = true,
onStartup = true,
onMove = true,
prepare = true,
source = true,
target = true,
}
--
-- On default action the user's on*** scripts are called.
--
proto.action = function
(
inlet -- the inlet of the active sync.
)
-- in case of moves getEvent returns the origin and dest of the move
local event, event2 = inlet.getEvent( )
local config = inlet.getConfig( )
local func = config[ 'on'.. event.etype ]
if type( func ) == 'function'
then
func( event, event2 )
end
-- if function didnt change the wait status its not interested
-- in this event -> drop it.
if event.status == 'wait'
then
inlet.discardEvent( event )
end
end
--
-- Default collector.
--
-- Called when collecting a finished child process
--
proto.collect = function
(
agent, -- event or event list being collected
exitcode -- the exitcode of the spawned process
)
local config = agent.config
local rc
if agent.syncStopped
then
log( 'Normal', 'Sync stopped, ignoring exitcode of finished child' )
return 'ok'
end
if config.exitcodes
then
rc = config.exitcodes[ exitcode ]
elseif exitcode == 0
then
rc = 'ok'
else
rc = 'die'
end
-- TODO synchronize with similar code before
if not agent.isList and agent.etype == 'Init'
then
if rc == 'ok'
then
log(
'Normal',
'Startup of ',agent.source, ' -> ',agent.target,' finished.'
)
return 'ok'
elseif rc == 'again'
then
if settings( 'insist' )
then
log(
'Normal',
'Retrying startup of ',
agent.source, ' -> ', agent.target,
': ', exitcode
)
return 'again'
end
elseif rc == 'die'
then
log(
'Error',
'Failure on startup of ',
agent.source, ' -> ', agent.target
)
terminate( -1 )
else
log(
'Error',
'Unknown exitcode "', exitcode,
'" on startup of ',
agent.source, ' -> ', agent.target
)
return 'die'
end
end
if agent.isList
then
if rc == 'ok'
then
log( 'Normal', 'Finished a list after exitcode: ', exitcode )
elseif rc == 'again'
then
log( 'Normal', 'Retrying a list after exitcode = ', exitcode )
elseif rc == 'die'
then
log( 'Error', 'Failure with a list with exitcode = ', exitcode )
else
log( 'Error', 'Unknown exitcode "', exitcode, '" with a list' )
rc = 'die'
end
else
if rc == 'ok'
then
log(
'Normal',
'Finished ', agent.etype,
' on ', agent.sourcePath, ' = ', exitcode
)
elseif rc == 'again'
then
log(
'Normal',
'Retrying ', agent.etype,
' on ', agent.sourcePath, ' = ', exitcode
)
elseif rc == 'die'
then
log( 'Error',
'Failure with ', agent.etype,
' on ', agent.sourcePath, ' = ', exitcode
)
else
log(
'Normal',
'Unknown exitcode "', exitcode,
'" with ', agent.etype,
' on ', agent.sourcePath, ' = ', exitcode
)
rc = 'die'
end
end
return rc
end
--
-- Called on the Init event sent
-- on (re)initialization of Lsyncd for every sync
--
proto.init = function
(
event -- the precreated init event.
)
local config = event.config
local inlet = event.inlet
-- user functions
-- calls a startup if given by user script.
if type( config.onStartup ) == 'function'
then
config.onStartup( event )
-- TODO honor some return codes of startup like "warmstart".
end
if event.status == 'wait'
then
-- user script did not spawn anything
-- thus the blanket event is deleted again.
inlet.discardEvent( event )
end
end
--
-- The collapsor tries not to have more than these delays.
-- So the delay queue does not grow too large
-- since calculation for stacking events is n*log( n ) (or so)
--
proto.maxDelays = 1000
--
-- The maximum number of processes Lsyncd will
-- simultanously spawn for this sync.
--
proto.maxProcesses = 1
--
-- Checks all keys to be in the checkgauge.
--
local function check
(
config,
gauge,
subtable,
level
)
for k, v in pairs( config )
do
if not gauge[ k ]
then
error(
'Parameter "' .. subtable .. k .. '" unknown.'
.. ' ( if this is not a typo add it to checkgauge )',
level
);
end
if type( gauge [ k ] ) == 'table'
then
if type( v ) ~= 'table'
then
error(
'Parameter "' .. subtable .. k .. '" must be a table.',
level
)
end
check(
config[ k ],
gauge[ k ],
subtable .. k .. '.',
level + 1
)
end
end
end
proto.prepare = function
(
config, -- the config to prepare for
level -- current callback level for error reporting
)
local gauge = config.checkgauge
if not gauge then return end
check( config, gauge, '', level + 1 )
end

View File

@ -31,7 +31,6 @@ default.rsync = rsync
-- used to ensure there aren't typos in the keys -- used to ensure there aren't typos in the keys
-- --
rsync.checkgauge = { rsync.checkgauge = {
-- unsets default user action handlers -- unsets default user action handlers
onCreate = false, onCreate = false,
onModify = false, onModify = false,
@ -628,14 +627,9 @@ rsync.delete = true
-- --
rsync.exitcodes = rsync.exitcodes =
{ {
--
-- if another config provides the same table -- if another config provides the same table
-- this will not be inherited (merged) into that one -- this will not be merged into that one
-- _merge = false,
-- if it does not, integer keys are to be copied
-- verbatim
--
_verbatim = true,
[ 0 ] = 'ok', [ 0 ] = 'ok',
[ 1 ] = 'die', [ 1 ] = 'die',

View File

@ -41,6 +41,8 @@ default.rsyncssh = rsyncssh
-- used to ensure there aren't typos in the keys -- used to ensure there aren't typos in the keys
-- --
rsyncssh.checkgauge = { rsyncssh.checkgauge = {
-- inherits the rsync checkgauge
default.rsync.checkgauge,
-- unsets the inherited value of from default.rsync -- unsets the inherited value of from default.rsync
target = false, target = false,
@ -577,13 +579,8 @@ rsyncssh.sshExitCodes =
{ {
-- --
-- if another config provides the same table -- if another config provides the same table
-- this will not be inherited (merged) into that one -- this will not be merged into that one
--
-- if it does not, integer keys are to be copied
-- verbatim
--
_merge = false, _merge = false,
_verbatim = true,
[ 0 ] = 'ok', [ 0 ] = 'ok',
[ 255 ] = 'again', [ 255 ] = 'again',

View File

@ -80,11 +80,40 @@ local function get
return syncList[ i ] return syncList[ i ]
end end
local inherit
-- --
-- Helper function for inherit -- Inherits the contents of all tables with array keys
-- defined below -- Returns the table with flattened inhertance,
-- also returns array size
-- --
local inheritKV --
local function flattenInheritance
(
t
)
local tf = { }
inherit( tf, t )
for k, v in ipairs( t )
do
-- numbers as key and table as value
-- means recursive inherit
if type( v ) == 'table'
then
local vv = flattenInheritance( v )
inherit( tf, vv )
else
if tf[ k ] == nil then tf[ k ] = v end
end
end
return tf
end
-- --
-- Recursevly inherits a source table to a destionation table -- Recursevly inherits a source table to a destionation table
@ -93,75 +122,52 @@ local inheritKV
-- All entries with integer keys are inherited as additional -- All entries with integer keys are inherited as additional
-- sources for non-verbatim tables -- sources for non-verbatim tables
-- --
local function inherit inherit = function
( (
cd, -- table copy destination cd, -- table copy destination
cs, -- table copy source cs -- table copy source
verbatim -- forced verbatim ( for e.g. 'exitcodes' )
) )
-- First copies all entries with non-integer keys. local imax = 0
--
-- Tables are merged; already present keys are not for k, _ in ipairs( cs ) do imax = k end
-- overwritten
--
-- For verbatim tables integer keys are treated like
-- non-integer keys
for k, v in pairs( cs ) for k, v in pairs( cs )
do do
if type( k ) ~= 'number' if type( k ) == 'number'
or verbatim
or cs._verbatim == true
then then
inheritKV( cd, k, v ) if( k < 1 or k > imax or math.floor( k ) ~= k )
end
end
-- recursevely inherits all integer keyed tables
-- ( for non-verbatim tables )
if cs._verbatim ~= true
then then
for k, v in ipairs( cs ) -- not an array integer
do
if type( v ) == 'table' if type( v ) == 'table'
then then
inherit( cd, v ) error( 'non sequence numeric key used as inheritance', 2 )
end
if cd[ k ] == nil then cd[ k ] = v end
end
else else
cd[ #cd + 1 ] = v
end
end
end
end
--
-- Helper to inherit. Inherits one key.
--
inheritKV =
function(
cd, -- table copy destination
k, -- key
v -- value
)
-- don't merge inheritance controls
if k == '_verbatim' then return end
local dtype = type( cd [ k ] )
if type( v ) == 'table' if type( v ) == 'table'
then then
if dtype == 'nil' v = flattenInheritance( v )
then
cd[ k ] = { }
inherit( cd[ k ], v, k == 'exitcodes' )
elseif dtype == 'table'
then
inherit( cd[ k ], v, k == 'exitcodes' )
end end
elseif dtype == 'nil'
local dv = cd[ k ]
if dv == nil
then then
cd[ k ] = v cd[ k ] = v
elseif type( dv ) == 'table'
and type( v ) == 'table'
and v._merge ~= false
then
dv = inherit( { }, dv )
dv = inherit( dv, v )
cd[ k ] = dv
end end
end
end
return cd
end end
@ -174,11 +180,7 @@ local function add
) )
-- Creates a new config table which inherits all keys/values -- Creates a new config table which inherits all keys/values
-- from integer keyed tables -- from integer keyed tables
local uconfig = config config = flattenInheritance( config )
config = { }
inherit( config, uconfig )
-- last and least default prototype is inherited -- last and least default prototype is inherited
inherit( config, userenv.default.proto ) inherit( config, userenv.default.proto )