flor 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/CREDITS.md +21 -0
- data/LICENSE.txt +4 -1
- data/Makefile +4 -0
- data/README.md +8 -0
- data/flor.gemspec +10 -10
- data/lib/flor.rb +2 -2
- data/lib/flor/changes.rb +3 -3
- data/lib/flor/colours.rb +14 -8
- data/lib/flor/conf.rb +63 -58
- data/lib/flor/core.rb +4 -4
- data/lib/flor/core/executor.rb +65 -29
- data/lib/flor/core/node.rb +37 -20
- data/lib/flor/core/procedure.rb +182 -40
- data/lib/flor/core/texecutor.rb +125 -52
- data/lib/flor/djan.rb +111 -82
- data/lib/flor/dollar.rb +31 -30
- data/lib/flor/flor.rb +314 -237
- data/lib/flor/id.rb +7 -2
- data/lib/flor/log.rb +250 -245
- data/lib/flor/parser.rb +72 -38
- data/lib/flor/pcore/_arr.rb +10 -10
- data/lib/flor/pcore/_att.rb +49 -14
- data/lib/flor/pcore/_coll.rb +18 -0
- data/lib/flor/pcore/_obj.rb +23 -7
- data/lib/flor/pcore/_pat_.rb +1 -1
- data/lib/flor/pcore/_pat_guard.rb +8 -0
- data/lib/flor/pcore/_pat_obj.rb +3 -3
- data/lib/flor/pcore/_pat_or.rb +4 -0
- data/lib/flor/pcore/_pat_regex.rb +24 -0
- data/lib/flor/pcore/_skip.rb +4 -0
- data/lib/flor/pcore/_val.rb +0 -1
- data/lib/flor/pcore/all.rb +111 -0
- data/lib/flor/pcore/any.rb +83 -0
- data/lib/flor/pcore/arith.rb +35 -6
- data/lib/flor/pcore/break.rb +39 -1
- data/lib/flor/pcore/case.rb +82 -4
- data/lib/flor/pcore/cmp.rb +7 -7
- data/lib/flor/pcore/collect.rb +50 -0
- data/lib/flor/pcore/cond.rb +17 -3
- data/lib/flor/pcore/cursor.rb +8 -2
- data/lib/flor/pcore/detect.rb +45 -0
- data/lib/flor/pcore/each.rb +52 -0
- data/lib/flor/pcore/empty.rb +60 -0
- data/lib/flor/pcore/filter.rb +94 -0
- data/lib/flor/pcore/find.rb +67 -0
- data/lib/flor/pcore/for_each.rb +65 -0
- data/lib/flor/pcore/includes.rb +32 -0
- data/lib/flor/pcore/inject.rb +55 -0
- data/lib/flor/pcore/iterator.rb +151 -0
- data/lib/flor/pcore/keys.rb +60 -0
- data/lib/flor/pcore/length.rb +34 -7
- data/lib/flor/pcore/logo.rb +18 -0
- data/lib/flor/pcore/loop.rb +4 -0
- data/lib/flor/pcore/map.rb +77 -46
- data/lib/flor/pcore/match.rb +8 -2
- data/lib/flor/pcore/matchr.rb +4 -5
- data/lib/flor/pcore/move.rb +3 -3
- data/lib/flor/pcore/noeval.rb +13 -0
- data/lib/flor/pcore/not.rb +16 -0
- data/lib/flor/pcore/on.rb +172 -0
- data/lib/flor/pcore/on_cancel.rb +54 -0
- data/lib/flor/pcore/on_error.rb +68 -0
- data/lib/flor/pcore/rand.rb +2 -2
- data/lib/flor/pcore/range.rb +2 -1
- data/lib/flor/pcore/reduce.rb +124 -0
- data/lib/flor/pcore/reverse.rb +46 -0
- data/lib/flor/pcore/select.rb +72 -0
- data/lib/flor/pcore/set.rb +8 -0
- data/lib/flor/pcore/stall.rb +10 -0
- data/lib/flor/pcore/to_array.rb +61 -0
- data/lib/flor/pcore/until.rb +34 -0
- data/lib/flor/punit/cancel.rb +30 -5
- data/lib/flor/punit/ccollect.rb +11 -0
- data/lib/flor/punit/cmap.rb +10 -5
- data/lib/flor/punit/concurrence.rb +42 -51
- data/lib/flor/punit/cron.rb +33 -0
- data/lib/flor/punit/do_trap.rb +42 -0
- data/lib/flor/punit/every.rb +48 -13
- data/lib/flor/punit/graft.rb +3 -3
- data/lib/flor/punit/on_timeout.rb +38 -0
- data/lib/flor/punit/schedule.rb +69 -6
- data/lib/flor/punit/signal.rb +54 -0
- data/lib/flor/punit/sleep.rb +1 -1
- data/lib/flor/punit/task.rb +4 -1
- data/lib/flor/punit/trap.rb +188 -13
- data/lib/flor/tools/shell.rb +408 -62
- data/lib/flor/tools/shell_out.rb +31 -0
- data/lib/flor/unit.rb +1 -1
- data/lib/flor/unit/caller.rb +177 -0
- data/lib/flor/unit/executor.rb +1 -0
- data/lib/flor/unit/ganger.rb +15 -21
- data/lib/flor/unit/hook.rb +1 -1
- data/lib/flor/unit/hooker.rb +22 -10
- data/lib/flor/unit/loader.rb +22 -22
- data/lib/flor/unit/logger.rb +63 -36
- data/lib/flor/unit/models.rb +6 -1
- data/lib/flor/unit/models/execution.rb +12 -1
- data/lib/flor/unit/models/message.rb +7 -0
- data/lib/flor/unit/models/trap.rb +31 -17
- data/lib/flor/unit/scheduler.rb +18 -10
- data/lib/flor/unit/storage.rb +83 -23
- data/lib/flor/unit/waiter.rb +1 -2
- metadata +96 -52
- data/lib/flor/deep.rb +0 -144
- data/lib/flor/punit/on.rb +0 -57
- data/lib/flor/unit/runner.rb +0 -84
- data/match.md +0 -22
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module Flor::Tools
|
3
|
+
|
4
|
+
class ShellOut
|
5
|
+
|
6
|
+
def initialize(unit)
|
7
|
+
|
8
|
+
@file = File.open('.log.txt', 'ab')
|
9
|
+
@file.sync = true
|
10
|
+
|
11
|
+
#@unit.conf['fls_file']
|
12
|
+
#@unit.conf['fls_file_mode']
|
13
|
+
end
|
14
|
+
|
15
|
+
def tty?
|
16
|
+
|
17
|
+
$stdout.tty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def puts(s)
|
21
|
+
|
22
|
+
$stdout.puts(s)
|
23
|
+
@file.puts(s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_missing(*args)
|
27
|
+
p [ 'MISSING>>>', args ]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
data/lib/flor/unit.rb
CHANGED
@@ -0,0 +1,177 @@
|
|
1
|
+
|
2
|
+
module Flor
|
3
|
+
|
4
|
+
# The caller calls Ruby or other scripts.
|
5
|
+
#
|
6
|
+
class Caller
|
7
|
+
|
8
|
+
# NB: tasker configuration entries start with "cal_"
|
9
|
+
|
10
|
+
def initialize(unit)
|
11
|
+
|
12
|
+
@unit = unit
|
13
|
+
end
|
14
|
+
|
15
|
+
def shutdown
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(service, conf, message)
|
19
|
+
|
20
|
+
return ruby_call(service, conf, message) \
|
21
|
+
if conf['class'] || conf['module']
|
22
|
+
return cmd_call(service, conf, message) \
|
23
|
+
if conf['cmd']
|
24
|
+
|
25
|
+
fail ArgumentError.new("don't know how to call item at #{conf['_path']}")
|
26
|
+
|
27
|
+
rescue => err
|
28
|
+
|
29
|
+
[ Flor.to_error(err) ]
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def conf
|
35
|
+
|
36
|
+
@unit ? @unit.conf : {}
|
37
|
+
end
|
38
|
+
|
39
|
+
def fjoin(root, path)
|
40
|
+
|
41
|
+
root == '.' ? path : File.join(root, path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def ruby_call(service, conf, message)
|
45
|
+
|
46
|
+
root = File.dirname(conf['_path'] || '.')
|
47
|
+
|
48
|
+
Flor.h_fetch_a(conf, 'require').each { |pa|
|
49
|
+
fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
|
50
|
+
require(fjoin(root, pa)) }
|
51
|
+
Flor.h_fetch_a(conf, 'load').each { |pa|
|
52
|
+
fail ArgumentError.new('".." not allowed in paths') if pa =~ /\.\./
|
53
|
+
load(fjoin(root, pa)) }
|
54
|
+
|
55
|
+
k = Flor.const_lookup(conf['class'] || conf['module'])
|
56
|
+
|
57
|
+
o =
|
58
|
+
if k.class == Module
|
59
|
+
k
|
60
|
+
else
|
61
|
+
case k.instance_method(:initialize).arity
|
62
|
+
when 1 then k.new(service)
|
63
|
+
when 2 then k.new(service, conf)
|
64
|
+
when 3 then k.new(service, conf, message)
|
65
|
+
when -1 then k.new({
|
66
|
+
service: service, configuration: conf, message: message })
|
67
|
+
else k.new
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
p = message['point']
|
72
|
+
m = :on
|
73
|
+
m = "on_#{p}" if ! o.respond_to?(m)
|
74
|
+
m = p if ! o.respond_to?(m)
|
75
|
+
m = :cancel if m == 'detask' && ! o.respond_to?(m)
|
76
|
+
|
77
|
+
fail(
|
78
|
+
"#{k.class.to_s.downcase} #{k} doesn't respond to on, on_#{p} or #{p}"
|
79
|
+
) if ! o.respond_to?(m)
|
80
|
+
|
81
|
+
r =
|
82
|
+
case o.method(m).arity
|
83
|
+
when 1 then o.send(m, message)
|
84
|
+
when 2 then o.send(m, conf, message)
|
85
|
+
when 3 then o.send(m, executor, conf, message)
|
86
|
+
when -1 then o.send(m, {
|
87
|
+
service: service, configuration: conf, message: message })
|
88
|
+
else o.send(m)
|
89
|
+
end
|
90
|
+
|
91
|
+
to_messages(r)
|
92
|
+
end
|
93
|
+
|
94
|
+
def cmd_call(service, conf, message)
|
95
|
+
|
96
|
+
h = conf.dup # shallow
|
97
|
+
h['m'] = message
|
98
|
+
h['f'] = message['payload']
|
99
|
+
h['v'] = message['vars']
|
100
|
+
h['tag'] = (message['tags'] || []).first
|
101
|
+
|
102
|
+
cmd = h['cmd']
|
103
|
+
|
104
|
+
m = encode(conf, message)
|
105
|
+
out, _ = spawn(cmd, m)
|
106
|
+
r = decode(conf, out)
|
107
|
+
|
108
|
+
to_messages(r)
|
109
|
+
end
|
110
|
+
|
111
|
+
def encode(context, message)
|
112
|
+
|
113
|
+
coder =
|
114
|
+
Flor.h_fetch(context, 'encoder', 'coder') ||
|
115
|
+
Flor.h_fetch(conf, 'cal_encoder', 'cal_coder') ||
|
116
|
+
'::JSON'
|
117
|
+
|
118
|
+
Flor.const_get(coder)
|
119
|
+
.dump(message)
|
120
|
+
end
|
121
|
+
|
122
|
+
def decode(context, data)
|
123
|
+
|
124
|
+
coder =
|
125
|
+
Flor.h_fetch(context, 'decoder', 'coder', 'encoder') ||
|
126
|
+
Flor.h_fetch(conf, 'cal_decoder', 'cal_coder', 'cal_encoder') ||
|
127
|
+
'::JSON'
|
128
|
+
|
129
|
+
Flor.const_get(coder)
|
130
|
+
.load(data)
|
131
|
+
end
|
132
|
+
|
133
|
+
def spawn(cmd, data)
|
134
|
+
|
135
|
+
i, o = IO.pipe # _ / stdout
|
136
|
+
f, e = IO.pipe # _ / stderr
|
137
|
+
r, w = IO.pipe # stdin / _
|
138
|
+
|
139
|
+
pid = Kernel.spawn(cmd, in: r, out: o, err: e)
|
140
|
+
w.write(data)
|
141
|
+
w.close
|
142
|
+
o.close
|
143
|
+
e.close
|
144
|
+
_, status = Process.wait2(pid)
|
145
|
+
|
146
|
+
fail SpawnError.new(status, i.read, f.read) if status.exitstatus != 0
|
147
|
+
|
148
|
+
[ i.read, status ]
|
149
|
+
end
|
150
|
+
|
151
|
+
class SpawnError < StandardError
|
152
|
+
|
153
|
+
attr_reader :status, :out, :err
|
154
|
+
|
155
|
+
def initialize(status, out, err)
|
156
|
+
|
157
|
+
@status = status
|
158
|
+
@out = out
|
159
|
+
@err = err
|
160
|
+
|
161
|
+
msg = err.strip.split("\n").last
|
162
|
+
|
163
|
+
super("(code: #{status.exitstatus}, pid: #{status.pid}) #{msg}")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_messages(o)
|
168
|
+
|
169
|
+
case o
|
170
|
+
when Hash then [ o ]
|
171
|
+
when Array then o
|
172
|
+
else []
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
data/lib/flor/unit/executor.rb
CHANGED
data/lib/flor/unit/ganger.rb
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
|
2
2
|
module Flor
|
3
3
|
|
4
|
+
# ganger | ˈɡaNGər | \ noun British \ the foreman of a gang of laborers.
|
5
|
+
#
|
6
|
+
# The ganger receives the tasks from the flor executor, decides what
|
7
|
+
# tasker will be invoked and hands it the task.
|
8
|
+
#
|
4
9
|
class Ganger
|
5
10
|
|
6
11
|
# NB: tasker configuration entries start with "gan_"
|
@@ -23,8 +28,10 @@ module Flor
|
|
23
28
|
|
24
29
|
d = Flor.domain(exid)
|
25
30
|
|
26
|
-
|
27
|
-
|
31
|
+
!! (
|
32
|
+
@unit.loader.tasker(d, 'ganger') ||
|
33
|
+
@unit.loader.tasker(d, 'tasker') ||
|
34
|
+
@unit.loader.tasker(d, name))
|
28
35
|
end
|
29
36
|
|
30
37
|
def task(executor, message)
|
@@ -33,10 +40,10 @@ module Flor
|
|
33
40
|
tname = message['tasker']
|
34
41
|
|
35
42
|
tconf =
|
36
|
-
( ! message['routed'] &&
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
( ! message['routed'] &&
|
44
|
+
(@unit.loader.tasker(domain, 'ganger', message) ||
|
45
|
+
@unit.loader.tasker(domain, 'tasker', message))) ||
|
46
|
+
@unit.loader.tasker(domain, tname, message)
|
40
47
|
|
41
48
|
fail ArgumentError.new(
|
42
49
|
"tasker #{tname.inspect} not found"
|
@@ -55,13 +62,7 @@ module Flor
|
|
55
62
|
|
56
63
|
message['vars'] = gather_vars(executor, tconf, message)
|
57
64
|
|
58
|
-
|
59
|
-
|
60
|
-
if is_a_message_array?(r)
|
61
|
-
r
|
62
|
-
else
|
63
|
-
[]
|
64
|
-
end
|
65
|
+
@unit.caller.call(self, tconf, message)
|
65
66
|
end
|
66
67
|
|
67
68
|
def return(message)
|
@@ -71,13 +72,6 @@ module Flor
|
|
71
72
|
|
72
73
|
protected
|
73
74
|
|
74
|
-
def is_a_message_array?(o)
|
75
|
-
|
76
|
-
o.is_a?(Array) &&
|
77
|
-
o.first.is_a?(Hash) &&
|
78
|
-
o.first['point'].is_a?(String)
|
79
|
-
end
|
80
|
-
|
81
75
|
def var_match(k, filter)
|
82
76
|
|
83
77
|
filter.each { |f| return true if (f.is_a?(String) ? k == f : f.match(k)) }
|
@@ -108,7 +102,7 @@ module Flor
|
|
108
102
|
|
109
103
|
def gather_vars(executor, tconf, message)
|
110
104
|
|
111
|
-
# try to return before
|
105
|
+
# try to return before a potentially costly call to executor.vars(nid)
|
112
106
|
|
113
107
|
return nil if (tconf.keys & %w[ include_vars exclude_vars ]).empty?
|
114
108
|
# default behaviour, don't pass variables to taskers
|
data/lib/flor/unit/hook.rb
CHANGED
data/lib/flor/unit/hooker.rb
CHANGED
@@ -40,9 +40,9 @@ module Flor
|
|
40
40
|
|
41
41
|
args.each do |arg|
|
42
42
|
case arg
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
when String then name = arg
|
44
|
+
when Hash then opts = arg
|
45
|
+
else hook = arg
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -56,7 +56,7 @@ module Flor
|
|
56
56
|
(
|
57
57
|
@hooks + executor.traps_and_hooks
|
58
58
|
)
|
59
|
-
.inject([])
|
59
|
+
.inject([]) { |a, (_, opts, hook, block)|
|
60
60
|
# name of hook is piped into "_" oblivion
|
61
61
|
|
62
62
|
a.concat(
|
@@ -68,8 +68,7 @@ module Flor
|
|
68
68
|
executor.trigger_hook(hook, message)
|
69
69
|
else
|
70
70
|
executor.trigger_block(block, opts, message)
|
71
|
-
end)
|
72
|
-
end
|
71
|
+
end) }
|
73
72
|
end
|
74
73
|
|
75
74
|
protected
|
@@ -86,6 +85,12 @@ module Flor
|
|
86
85
|
array ? Array(r) : r
|
87
86
|
end
|
88
87
|
|
88
|
+
def includes?(arr, value)
|
89
|
+
|
90
|
+
Array(value)
|
91
|
+
.find { |v| arr.find { |e| e.is_a?(Regexp) ? e.match(v) : e == v } }
|
92
|
+
end
|
93
|
+
|
89
94
|
def match?(executor, hook, opts, message)
|
90
95
|
|
91
96
|
opts = hook.opts if hook.respond_to?(:opts) && opts.empty?
|
@@ -128,11 +133,18 @@ module Flor
|
|
128
133
|
|
129
134
|
if ts = o(opts, :tag, :t, [])
|
130
135
|
return false unless %w[ entered left ].include?(message['point'])
|
131
|
-
return false unless (message['tags']
|
136
|
+
return false unless includes?(ts, message['tags'])
|
132
137
|
end
|
133
138
|
|
134
139
|
if ns = o(opts, :name, :n)
|
135
|
-
|
140
|
+
name = message['name']
|
141
|
+
return false \
|
142
|
+
unless ns.find { |n|
|
143
|
+
case n
|
144
|
+
when String then name == n
|
145
|
+
when Array then name.match(Flor.to_regex(n))
|
146
|
+
else false
|
147
|
+
end }
|
136
148
|
end
|
137
149
|
|
138
150
|
node = nil
|
@@ -148,12 +160,12 @@ module Flor
|
|
148
160
|
|
149
161
|
if hps = o(opts, :heap, :hp, [])
|
150
162
|
return false unless node ||= executor.node(message['nid'])
|
151
|
-
return false unless
|
163
|
+
return false unless includes?(hps, node['heap'])
|
152
164
|
end
|
153
165
|
|
154
166
|
if hts = o(opts, :heat, :ht, [])
|
155
167
|
return false unless node ||= executor.node(message['nid'])
|
156
|
-
return false unless
|
168
|
+
return false unless includes?(hts, node['heat0'])
|
157
169
|
end
|
158
170
|
|
159
171
|
true
|
data/lib/flor/unit/loader.rb
CHANGED
@@ -25,7 +25,7 @@ module Flor
|
|
25
25
|
.collect { |pa| [ pa, expose_d(pa, {}) ] }
|
26
26
|
.select { |pa, d| is_subdomain?(domain, d) }
|
27
27
|
.sort_by { |pa, d| d.count('.') }
|
28
|
-
.inject({}) { |vars, (pa, d)| vars.merge!(
|
28
|
+
.inject({}) { |vars, (pa, d)| vars.merge!(eval(pa, {})) }
|
29
29
|
end
|
30
30
|
|
31
31
|
#def procedures(path)
|
@@ -55,7 +55,7 @@ module Flor
|
|
55
55
|
path ? [ Flor.relativize_path(path), File.read(path) ] : nil
|
56
56
|
end
|
57
57
|
|
58
|
-
def tasker(domain, name=
|
58
|
+
def tasker(domain, name, message={})
|
59
59
|
|
60
60
|
# NB: do not relativize path, because Ruby load path != cwd,
|
61
61
|
# stay absolute for `require` and `load`
|
@@ -73,23 +73,18 @@ module Flor
|
|
73
73
|
|
74
74
|
return nil unless path
|
75
75
|
|
76
|
-
conf =
|
76
|
+
conf = eval(path, message)
|
77
77
|
|
78
78
|
return conf if n == name
|
79
79
|
|
80
|
-
|
80
|
+
conf = conf[name]
|
81
81
|
|
82
|
-
return nil unless
|
82
|
+
return nil unless conf
|
83
83
|
|
84
|
-
|
84
|
+
(conf.is_a?(Array) ? conf : [ conf ])
|
85
|
+
.each { |h| h['_path'] = path }
|
85
86
|
|
86
|
-
|
87
|
-
con.each { |c| c['_path'] = pa }
|
88
|
-
else
|
89
|
-
con['_path'] = pa
|
90
|
-
end
|
91
|
-
|
92
|
-
con
|
87
|
+
conf
|
93
88
|
end
|
94
89
|
|
95
90
|
def hooks(domain, name=nil)
|
@@ -103,7 +98,7 @@ module Flor
|
|
103
98
|
.select { |pa, d| is_subdomain?(domain, d) }
|
104
99
|
.sort_by { |pa, d| d.count('.') }
|
105
100
|
.collect { |pa, d|
|
106
|
-
|
101
|
+
eval(pa, {}).each_with_index { |h, i|
|
107
102
|
h['_path'] = pa + ":#{i}" } }
|
108
103
|
.flatten(1)
|
109
104
|
end
|
@@ -166,17 +161,22 @@ module Flor
|
|
166
161
|
end
|
167
162
|
end
|
168
163
|
|
169
|
-
def
|
164
|
+
def eval(path, context)
|
170
165
|
|
171
|
-
|
166
|
+
src =
|
167
|
+
@mutex.synchronize do
|
172
168
|
|
173
|
-
|
174
|
-
|
175
|
-
#p [ :cached, path ] if val && mt1 == mt0
|
176
|
-
return val if val && mt1 == mt0
|
169
|
+
mt1 = File.mtime(path)
|
170
|
+
val, mt0 = @cache[path]
|
177
171
|
|
178
|
-
|
179
|
-
|
172
|
+
if val && mt1 == mt0
|
173
|
+
val
|
174
|
+
else
|
175
|
+
(@cache[path] = [ Flor::ConfExecutor.load(path), mt1 ]).first
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
Flor::ConfExecutor.interpret(path, src, context)
|
180
180
|
end
|
181
181
|
end
|
182
182
|
end
|