flor 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -53,7 +53,9 @@ module Flor
53
53
 
54
54
  def notify(executor, message)
55
55
 
56
- (@hooks + executor.traps.collect(&:to_hook))
56
+ (
57
+ @hooks + executor.traps_and_hooks
58
+ )
57
59
  .inject([]) do |a, (_, opts, hook, block)|
58
60
  # name of hook is piped into "_" oblivion
59
61
 
@@ -64,17 +66,8 @@ module Flor
64
66
  executor.trigger_trap(hook, message)
65
67
  elsif hook
66
68
  executor.trigger_hook(hook, message)
67
- else # if block
68
- r =
69
- if block.arity == 1
70
- block.call(message)
71
- elsif block.arity == 2
72
- block.call(message, opts)
73
- else
74
- block.call(executor, message, opts)
75
- end
76
- r.is_a?(Array) && r.all? { |e| e.is_a?(Hash) } ? r : []
77
- # be lenient with block hooks, help them return an array
69
+ else
70
+ executor.trigger_block(block, opts, message)
78
71
  end)
79
72
  end
80
73
  end
@@ -0,0 +1,37 @@
1
+
2
+ module Flor
3
+
4
+ class BasicHook
5
+
6
+ def initialize(executor, opts, message)
7
+
8
+ @executor = executor
9
+ @opts = opts
10
+ @message = message
11
+ end
12
+
13
+ protected
14
+
15
+ def point
16
+
17
+ @message['point']
18
+ end
19
+ end
20
+
21
+ class PointHook < BasicHook
22
+
23
+ def on
24
+
25
+ m = "on_#{point}"
26
+
27
+ respond_to?(m) ? send(m) : on_default
28
+ end
29
+
30
+ protected
31
+
32
+ def on_default
33
+
34
+ []
35
+ end
36
+ end
37
+ end
@@ -11,6 +11,8 @@ module Flor
11
11
 
12
12
  @cache = {}
13
13
  @mutex = Mutex.new
14
+
15
+ @root = File.absolute_path(@unit.conf['lod_path'] || @unit.conf['root'])
14
16
  end
15
17
 
16
18
  def shutdown
@@ -18,7 +20,7 @@ module Flor
18
20
 
19
21
  def variables(domain)
20
22
 
21
- Dir[File.join(root, '**/*.json')]
23
+ Dir[File.join(@root, '**/*.json')]
22
24
  .select { |f| f.index('/etc/variables/') }
23
25
  .collect { |pa| [ pa, expose_d(pa, {}) ] }
24
26
  .select { |pa, d| is_subdomain?(domain, d) }
@@ -39,7 +41,11 @@ module Flor
39
41
  domain, name, opts = [ domain, nil, name ] if name.is_a?(Hash)
40
42
  domain, name = split_dn(domain, name)
41
43
 
42
- path, d, n = (Dir[File.join(root, '**/*.{flo,flor}')])
44
+ if m = name.match(/(\.flor?)\z/)
45
+ name = name[0..m[1].length - 1]
46
+ end
47
+
48
+ path, d, n = (Dir[File.join(@root, '**/*.{flo,flor}')])
43
49
  .select { |f| f.index('/lib/') }
44
50
  .collect { |pa| [ pa, *expose_dn(pa, opts) ] }
45
51
  .select { |pa, d, n| n == name && is_subdomain?(domain, d) }
@@ -51,9 +57,12 @@ module Flor
51
57
 
52
58
  def tasker(domain, name=nil)
53
59
 
60
+ # NB: do not relativize path, because Ruby load path != cwd,
61
+ # stay absolute for `require` and `load`
62
+
54
63
  domain, name = split_dn(domain, name)
55
64
 
56
- path, d, n = Dir[File.join(root, '**/*.json')]
65
+ path, d, n = Dir[File.join(@root, '**/*.json')]
57
66
  .select { |pa| pa.index('/lib/taskers/') }
58
67
  .collect { |pa| [ pa, *expose_dn(pa, {}) ] }
59
68
  .select { |pa, d, n|
@@ -72,7 +81,34 @@ module Flor
72
81
 
73
82
  return nil unless con
74
83
 
75
- con.merge!('_path' => conf['_path'])
84
+ pa = conf['_path']
85
+
86
+ if con.is_a?(Array)
87
+ con.each { |c| c['_path'] = pa }
88
+ else
89
+ con['_path'] = pa
90
+ end
91
+
92
+ con
93
+ end
94
+
95
+ def hooks(domain, name=nil)
96
+
97
+ Dir[File.join(@root, '**/*.json')]
98
+ .select { |f| f.index('/lib/hooks/') }
99
+ .collect { |pa| [ pa, expose_d(pa, {}) ] }
100
+ .select { |pa, d| is_subdomain?(domain, d) }
101
+ .sort_by { |pa, d| d.count('.') }
102
+ .collect { |pa, d|
103
+ interpret(pa).each_with_index { |h, i|
104
+ h['_path'] = Flor.relativize_path(pa) + ":#{i}" } }
105
+ .flatten(1)
106
+ end
107
+
108
+ def load_hooks(exid)
109
+
110
+ hooks(Flor.domain(exid))
111
+ .collect { |h| Flor::Hook.new(@unit, exid, h) }
76
112
  end
77
113
 
78
114
  protected
@@ -98,19 +134,19 @@ module Flor
98
134
 
99
135
  def expose_d(path, opts)
100
136
 
101
- pa = path[root.length..-1]
137
+ pa = path[@root.length..-1]
102
138
  pa = pa[5..-1] if pa[0, 5] == '/usr/'
103
139
 
104
140
  libregex =
105
141
  opts[:subflows] ?
106
- /\/lib\/(subflows|flows|taskers)\// :
107
- /\/lib\/(flows|taskers)\//
142
+ /\/lib\/(subflows|flows|hooks|taskers)\// :
143
+ /\/lib\/(flows|hooks|taskers)\//
108
144
 
109
145
  pa
110
146
  .sub(/\/etc\/variables\//, '/')
111
147
  .sub(libregex, '/')
112
148
  .sub(/\/\z/, '')
113
- .sub(/\/(flo|flor|dot)\.json\z/, '')
149
+ .sub(/\/(flo|flor|dot|hooks)\.json\z/, '')
114
150
  .sub(/\.(flo|flor|json)\z/, '')
115
151
  .sub(/\A\//, '')
116
152
  .gsub(/\//, '.')
@@ -127,15 +163,6 @@ module Flor
127
163
  end
128
164
  end
129
165
 
130
- def root
131
-
132
- if lp = @unit.conf['lod_path']
133
- File.absolute_path(lp)
134
- else
135
- File.dirname(File.absolute_path(@unit.conf['_path'] + '/..'))
136
- end
137
- end
138
-
139
166
  def interpret(path)
140
167
 
141
168
  @mutex.synchronize do
@@ -14,6 +14,7 @@ module Flor
14
14
  def initialize(unit)
15
15
 
16
16
  @unit = unit
17
+ @unit.hooker.add('logger', self)
17
18
 
18
19
  @out = prepare_out
19
20
 
@@ -5,33 +5,29 @@ module Flor
5
5
 
6
6
  def to_hook
7
7
 
8
- @hook ||=
9
- begin
10
-
11
- opts = {}
12
-
13
- opts[:consumed] = tconsumed
14
-
15
- opts[:point] = tpoints.split(',') if tpoints
16
- opts[:heap] = theaps.split(',') if theaps
17
- opts[:heat] = theats.split(',') if theats
18
-
19
- opts[:name] = data['names']
20
-
21
- case trange
22
- when 'execution'
23
- opts[:exid] = exid
24
- when 'subdomain'
25
- opts[:subdomain] = Flor.domain(exid)
26
- when 'domain'
27
- opts[:domain] = Flor.domain(exid)
28
- else #'subnid' # default
29
- opts[:exid] = exid
30
- opts[:subnid] = true
31
- end
8
+ opts = {}
9
+
10
+ opts[:consumed] = tconsumed
11
+
12
+ opts[:point] = tpoints.split(',') if tpoints
13
+ opts[:heap] = theaps.split(',') if theaps
14
+ opts[:heat] = theats.split(',') if theats
15
+
16
+ opts[:name] = data['names']
17
+
18
+ case trange
19
+ when 'execution'
20
+ opts[:exid] = exid
21
+ when 'subdomain'
22
+ opts[:subdomain] = Flor.domain(exid)
23
+ when 'domain'
24
+ opts[:domain] = Flor.domain(exid)
25
+ else #'subnid' # default
26
+ opts[:exid] = exid
27
+ opts[:subnid] = true
28
+ end
32
29
 
33
- [ "trap#{id}", opts, self, nil ]
34
- end
30
+ [ "trap#{id}", opts, self, nil ]
35
31
  end
36
32
 
37
33
  def trigger(executor, message)
@@ -0,0 +1,84 @@
1
+
2
+ module Flor
3
+
4
+ class Runner
5
+
6
+ # NB: tasker configuration entries start with "rnr_"
7
+
8
+ def initialize(unit)
9
+
10
+ @unit = unit
11
+ end
12
+
13
+ def shutdown
14
+ end
15
+
16
+ def run(service, conf, message)
17
+
18
+ return ruby_run(service, conf, message) if conf['class'] || conf['module']
19
+ return cmd_run(service, conf, message) if conf['cmd']
20
+
21
+ fail ArgumentError.new("don't know how to run item at #{conf['_path']}")
22
+ end
23
+
24
+ protected
25
+
26
+ def fjoin(root, path)
27
+
28
+ root == '.' ? path : File.join(root, path)
29
+ end
30
+
31
+ def ruby_run(service, conf, message)
32
+
33
+ root = File.dirname(conf['_path'])
34
+
35
+ Flor.h_fetch_a(conf, 'require').each { |pa|
36
+ fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
37
+ require(fjoin(root, pa)) }
38
+ Flor.h_fetch_a(conf, 'load').each { |pa|
39
+ fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
40
+ load(fjoin(root, pa)) }
41
+
42
+ k = Flor.const_lookup(conf['class'] || conf['module'])
43
+
44
+ o =
45
+ if k.class == Module
46
+ k
47
+ else
48
+ case k.instance_method(:initialize).arity
49
+ when 1 then k.new(service)
50
+ when 2 then k.new(service, conf)
51
+ when 3 then k.new(service, conf, message)
52
+ when -1 then k.new({
53
+ service: service, configuration: conf, message: message })
54
+ else k.new
55
+ end
56
+ end
57
+
58
+ p = message['point']
59
+ m = :on
60
+ m = "on_#{p}" if ! o.respond_to?(m)
61
+ m = p if ! o.respond_to?(m)
62
+ m = :cancel if m == 'detask' && ! o.respond_to?(m)
63
+
64
+ fail(
65
+ "#{k.class.to_s.downcase} #{k} doesn't respond to on, on_#{p} or #{p}"
66
+ ) if ! o.respond_to?(m)
67
+
68
+ case o.method(m).arity
69
+ when 1 then o.send(m, message)
70
+ when 2 then o.send(m, conf, message)
71
+ when 3 then o.send(m, executor, conf, message)
72
+ when -1 then o.send(m, {
73
+ service: service, configuration: conf, message: message })
74
+ else o.send(m)
75
+ end
76
+ end
77
+
78
+ def cmd_run(service, conf, message)
79
+
80
+ fail NotImplementedError
81
+ end
82
+ end
83
+ end
84
+
@@ -5,7 +5,7 @@ module Flor
5
5
 
6
6
  attr_reader :conf, :env
7
7
 
8
- attr_reader :hooker, :storage, :loader, :ganger
8
+ attr_reader :hooker, :storage, :loader, :ganger, :runner
9
9
  attr_reader :logger
10
10
 
11
11
  attr_reader :thread_status
@@ -27,20 +27,24 @@ module Flor
27
27
  @env = (Kernel.const_get(@env) rescue @env) if @env.match(/\A[A-Z]+\z/)
28
28
  # when env is "RAILS_ENV" for example...
29
29
 
30
+ @loader =
31
+ (Flor::Conf.get_class(@conf, 'loader') || Flor::Loader).new(self)
32
+ @runner =
33
+ (Flor::Conf.get_class(@conf, 'runner') || Flor::Runner).new(self)
30
34
  @hooker =
31
35
  (Flor::Conf.get_class(@conf, 'hooker') || Flor::Hooker).new(self)
32
36
  @storage =
33
37
  (Flor::Conf.get_class(@conf, 'storage') || Flor::Storage).new(self)
34
- @loader =
35
- (Flor::Conf.get_class(@conf, 'loader') || Flor::Loader).new(self)
36
38
  @ganger =
37
39
  (Flor::Conf.get_class(@conf, 'ganger') || Flor::Ganger).new(self)
38
40
 
39
41
  @logger =
40
42
  (Flor::Conf.get_class(@conf, 'logger') || Flor::Logger).new(self)
43
+ @wlist =
44
+ (Flor::Conf.get_class(@conf, 'wlist') || Flor::WaitList).new(self)
41
45
 
42
- @hooker.add('logger', @logger)
43
- @hooker.add('wlist', Flor::WaitList)
46
+ @spooler =
47
+ (Flor::Conf.get_class(@conf, 'spooler') || Flor::Spooler).new(self)
44
48
 
45
49
  @heart_rate = @conf[:sch_heart_rate] || 0.3
46
50
  @reload_after = @conf[:sch_reload_after] || 60
@@ -82,7 +86,7 @@ module Flor
82
86
  Socket.ip_address_list.find { |a| a.ipv4_private? } ||
83
87
  Socket.ip_address_list.find { |a| a.ip_address != '::1' }
84
88
  ip =
85
- ai ? ai.ip_address : '::1'
89
+ (ai ? ai.ip_address : '::1').split('%').first
86
90
  [
87
91
  'sch', self.name,
88
92
  'i' + ip,
@@ -149,46 +153,13 @@ module Flor
149
153
 
150
154
  @thread =
151
155
  if @thread
152
-
153
156
  @thread.run
154
-
155
157
  else
156
-
157
158
  Thread.new do
158
-
159
159
  loop do
160
-
161
- begin
162
-
163
- Thread.stop if @thread_status == :stop
164
- break if @thread_status == :shutdown
165
-
166
- t0 = Time.now
167
-
168
- if should_wake_up?
169
-
170
- unreserve_messages
171
-
172
- trigger_timers
173
- trigger_executions
174
-
175
- reload_next_time
176
- reload_wake_up
177
- @reloaded_at = Time.now
178
-
179
- elsif @executors.empty?
180
-
181
- @idle_count += 1
182
- notify(nil, make_idle_message)
183
- end
184
-
185
- sleep [ @heart_rate - (Time.now - t0), 0 ].max #\
186
- #unless should_wake_up?
187
-
188
- rescue Exception => ex
189
-
190
- puts on_start_exc(ex)
191
- end
160
+ Thread.stop if @thread_status == :stop
161
+ break if @thread_status == :shutdown
162
+ tick
192
163
  end
193
164
  end
194
165
  end
@@ -360,6 +331,37 @@ module Flor
360
331
 
361
332
  protected
362
333
 
334
+ def tick
335
+
336
+ t0 = Time.now
337
+
338
+ spool
339
+
340
+ if should_wake_up?
341
+
342
+ unreserve_messages
343
+
344
+ trigger_timers
345
+ trigger_executions
346
+
347
+ reload_next_time
348
+ reload_wake_up
349
+ @reloaded_at = Time.now
350
+
351
+ elsif @executors.empty?
352
+
353
+ @idle_count += 1
354
+ notify(nil, make_idle_message)
355
+ end
356
+
357
+ sleep [ @heart_rate - (Time.now - t0), 0 ].max #\
358
+ #unless should_wake_up?
359
+
360
+ rescue Exception => ex
361
+
362
+ puts on_start_exc(ex)
363
+ end
364
+
363
365
  def prepare_on_receive_last(h)
364
366
 
365
367
  ei = h[:exid] || h['exid']
@@ -409,14 +411,17 @@ module Flor
409
411
  m
410
412
  end
411
413
 
412
- def should_wake_up?
414
+ def reload_after
415
+
416
+ @idle_count < 5 ? 1 : @reload_after
417
+ end
413
418
 
414
- return true if Time.now - @reloaded_at >= @reload_after
419
+ def should_wake_up?
415
420
 
416
421
  return true if @wake_up
417
- return false unless @next_time
422
+ return true if Time.now - @reloaded_at >= reload_after
418
423
 
419
- @next_time <= Flor.tstamp.split('.').first
424
+ @next_time && @next_time <= Flor.tstamp.split('.').first
420
425
  end
421
426
 
422
427
  def unreserve_messages
@@ -428,6 +433,12 @@ module Flor
428
433
  ) if c > 0
429
434
  end
430
435
 
436
+ def spool
437
+
438
+ count = @spooler.spool
439
+ @wake_up = true if count > 0
440
+ end
441
+
431
442
  def trigger_timers
432
443
 
433
444
  @storage.trigger_timers