ace-eye 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CHANGES.md +77 -0
- data/Gemfile +6 -0
- data/LICENSE +22 -0
- data/README.md +212 -0
- data/Rakefile +35 -0
- data/bin/eye +5 -0
- data/bin/loader_eye +72 -0
- data/bin/runner +16 -0
- data/examples/dependency.eye +17 -0
- data/examples/notify.eye +19 -0
- data/examples/plugin/README.md +15 -0
- data/examples/plugin/main.eye +15 -0
- data/examples/plugin/plugin.rb +63 -0
- data/examples/process_thin.rb +29 -0
- data/examples/processes/em.rb +57 -0
- data/examples/processes/forking.rb +20 -0
- data/examples/processes/sample.rb +144 -0
- data/examples/processes/thin.ru +12 -0
- data/examples/puma.eye +29 -0
- data/examples/rbenv.eye +11 -0
- data/examples/sidekiq.eye +23 -0
- data/examples/test.eye +87 -0
- data/examples/thin-farm.eye +30 -0
- data/examples/unicorn.eye +39 -0
- data/eye.gemspec +40 -0
- data/lib/eye.rb +28 -0
- data/lib/eye/application.rb +73 -0
- data/lib/eye/checker.rb +258 -0
- data/lib/eye/checker/children_count.rb +44 -0
- data/lib/eye/checker/children_memory.rb +12 -0
- data/lib/eye/checker/cpu.rb +17 -0
- data/lib/eye/checker/cputime.rb +13 -0
- data/lib/eye/checker/file_ctime.rb +24 -0
- data/lib/eye/checker/file_size.rb +34 -0
- data/lib/eye/checker/file_touched.rb +15 -0
- data/lib/eye/checker/http.rb +96 -0
- data/lib/eye/checker/memory.rb +17 -0
- data/lib/eye/checker/nop.rb +6 -0
- data/lib/eye/checker/runtime.rb +18 -0
- data/lib/eye/checker/socket.rb +159 -0
- data/lib/eye/child_process.rb +101 -0
- data/lib/eye/cli.rb +185 -0
- data/lib/eye/cli/commands.rb +78 -0
- data/lib/eye/cli/render.rb +130 -0
- data/lib/eye/cli/server.rb +93 -0
- data/lib/eye/client.rb +32 -0
- data/lib/eye/config.rb +91 -0
- data/lib/eye/control.rb +2 -0
- data/lib/eye/controller.rb +54 -0
- data/lib/eye/controller/commands.rb +88 -0
- data/lib/eye/controller/helpers.rb +101 -0
- data/lib/eye/controller/load.rb +224 -0
- data/lib/eye/controller/options.rb +18 -0
- data/lib/eye/controller/send_command.rb +177 -0
- data/lib/eye/controller/status.rb +72 -0
- data/lib/eye/dsl.rb +53 -0
- data/lib/eye/dsl/application_opts.rb +39 -0
- data/lib/eye/dsl/chain.rb +12 -0
- data/lib/eye/dsl/child_process_opts.rb +13 -0
- data/lib/eye/dsl/config_opts.rb +55 -0
- data/lib/eye/dsl/group_opts.rb +32 -0
- data/lib/eye/dsl/helpers.rb +20 -0
- data/lib/eye/dsl/main.rb +51 -0
- data/lib/eye/dsl/opts.rb +151 -0
- data/lib/eye/dsl/process_opts.rb +36 -0
- data/lib/eye/dsl/pure_opts.rb +121 -0
- data/lib/eye/dsl/validation.rb +88 -0
- data/lib/eye/group.rb +140 -0
- data/lib/eye/group/chain.rb +81 -0
- data/lib/eye/loader.rb +10 -0
- data/lib/eye/local.rb +100 -0
- data/lib/eye/logger.rb +104 -0
- data/lib/eye/notify.rb +118 -0
- data/lib/eye/notify/jabber.rb +30 -0
- data/lib/eye/notify/mail.rb +48 -0
- data/lib/eye/process.rb +85 -0
- data/lib/eye/process/children.rb +60 -0
- data/lib/eye/process/commands.rb +280 -0
- data/lib/eye/process/config.rb +81 -0
- data/lib/eye/process/controller.rb +73 -0
- data/lib/eye/process/data.rb +78 -0
- data/lib/eye/process/monitor.rb +108 -0
- data/lib/eye/process/notify.rb +32 -0
- data/lib/eye/process/scheduler.rb +82 -0
- data/lib/eye/process/states.rb +86 -0
- data/lib/eye/process/states_history.rb +66 -0
- data/lib/eye/process/system.rb +97 -0
- data/lib/eye/process/trigger.rb +34 -0
- data/lib/eye/process/validate.rb +33 -0
- data/lib/eye/process/watchers.rb +66 -0
- data/lib/eye/reason.rb +20 -0
- data/lib/eye/server.rb +60 -0
- data/lib/eye/sigar.rb +5 -0
- data/lib/eye/system.rb +139 -0
- data/lib/eye/system_resources.rb +99 -0
- data/lib/eye/trigger.rb +136 -0
- data/lib/eye/trigger/check_dependency.rb +30 -0
- data/lib/eye/trigger/flapping.rb +41 -0
- data/lib/eye/trigger/stop_children.rb +17 -0
- data/lib/eye/trigger/transition.rb +15 -0
- data/lib/eye/trigger/wait_dependency.rb +49 -0
- data/lib/eye/utils.rb +45 -0
- data/lib/eye/utils/alive_array.rb +57 -0
- data/lib/eye/utils/celluloid_chain.rb +71 -0
- data/lib/eye/utils/celluloid_klass.rb +5 -0
- data/lib/eye/utils/leak_19.rb +10 -0
- data/lib/eye/utils/mini_active_support.rb +111 -0
- data/lib/eye/utils/pmap.rb +7 -0
- data/lib/eye/utils/tail.rb +20 -0
- metadata +398 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
|
3
|
+
class Eye::ChildProcess
|
4
|
+
include Celluloid
|
5
|
+
|
6
|
+
# needs: kill_process
|
7
|
+
include Eye::Process::Commands
|
8
|
+
|
9
|
+
# easy config + defaults: prepare_config, c, []
|
10
|
+
include Eye::Process::Config
|
11
|
+
|
12
|
+
# conditional watchers: start_checkers
|
13
|
+
include Eye::Process::Watchers
|
14
|
+
|
15
|
+
# system methods: send_signal
|
16
|
+
include Eye::Process::System
|
17
|
+
|
18
|
+
# self_status_data
|
19
|
+
include Eye::Process::Data
|
20
|
+
|
21
|
+
# manage notify methods
|
22
|
+
include Eye::Process::Notify
|
23
|
+
|
24
|
+
# scheduler
|
25
|
+
include Eye::Process::Scheduler
|
26
|
+
|
27
|
+
attr_reader :pid, :name, :full_name, :config, :watchers
|
28
|
+
|
29
|
+
def initialize(pid, config = {}, logger_prefix = nil, parent_pid = nil)
|
30
|
+
raise 'Empty pid' unless pid
|
31
|
+
|
32
|
+
@pid = pid
|
33
|
+
@parent_pid = parent_pid
|
34
|
+
@config = prepare_config(config)
|
35
|
+
@name = "child-#{pid}"
|
36
|
+
@full_name = [logger_prefix, @name] * ':'
|
37
|
+
|
38
|
+
@watchers = {}
|
39
|
+
|
40
|
+
debug "start monitoring CHILD config: #{@config.inspect}"
|
41
|
+
|
42
|
+
start_checkers
|
43
|
+
end
|
44
|
+
|
45
|
+
def logger_tag
|
46
|
+
full_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def state
|
50
|
+
:up
|
51
|
+
end
|
52
|
+
|
53
|
+
def up?
|
54
|
+
state == :up
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_command(command, *args)
|
58
|
+
schedule command, *args, Eye::Reason::User.new(command)
|
59
|
+
end
|
60
|
+
|
61
|
+
def start
|
62
|
+
end
|
63
|
+
|
64
|
+
def stop
|
65
|
+
kill_process
|
66
|
+
end
|
67
|
+
|
68
|
+
def restart
|
69
|
+
if self[:restart_command]
|
70
|
+
execute_restart_command
|
71
|
+
else
|
72
|
+
stop
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def monitor
|
77
|
+
end
|
78
|
+
|
79
|
+
def unmonitor
|
80
|
+
end
|
81
|
+
|
82
|
+
def delete
|
83
|
+
end
|
84
|
+
|
85
|
+
def destroy
|
86
|
+
remove_watchers
|
87
|
+
terminate
|
88
|
+
end
|
89
|
+
|
90
|
+
def signal(sig)
|
91
|
+
send_signal(sig) if self.pid
|
92
|
+
end
|
93
|
+
|
94
|
+
def status_data(debug = false)
|
95
|
+
self_status_data(debug)
|
96
|
+
end
|
97
|
+
|
98
|
+
def prepare_command(command) # override
|
99
|
+
super.gsub('{PARENT_PID}', @parent_pid.to_s)
|
100
|
+
end
|
101
|
+
end
|
data/lib/eye/cli.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
gem 'thor'
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
class Eye::Cli < Thor
|
5
|
+
autoload :Server, 'eye/cli/server'
|
6
|
+
autoload :Commands, 'eye/cli/commands'
|
7
|
+
autoload :Render, 'eye/cli/render'
|
8
|
+
|
9
|
+
include Eye::Cli::Server
|
10
|
+
include Eye::Cli::Commands
|
11
|
+
include Eye::Cli::Render
|
12
|
+
|
13
|
+
desc "info [MASK]", "processes info"
|
14
|
+
def info(mask = nil)
|
15
|
+
res = cmd(:info_data, *Array(mask))
|
16
|
+
if mask && res[:subtree] && res[:subtree].empty?
|
17
|
+
error!("command :info, objects not found!")
|
18
|
+
end
|
19
|
+
say render_info(res)
|
20
|
+
say
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "status", "processes info (deprecated)"
|
24
|
+
def status
|
25
|
+
say ":status is deprecated, use :info instead", :yellow
|
26
|
+
info
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "xinfo", "eye-deamon info (-c show current config)"
|
30
|
+
method_option :config, :type => :boolean, :aliases => "-c"
|
31
|
+
def xinfo
|
32
|
+
res = cmd(:debug_data, :config => options[:config])
|
33
|
+
say render_debug_info(res)
|
34
|
+
say
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "oinfo", "onelined info"
|
38
|
+
def oinfo(mask = nil)
|
39
|
+
res = cmd(:short_data, *Array(mask))
|
40
|
+
say render_info(res)
|
41
|
+
say
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "history [MASK,...]", "processes history"
|
45
|
+
def history(*masks)
|
46
|
+
res = cmd(:history_data, *masks)
|
47
|
+
if !masks.empty? && res && res.empty?
|
48
|
+
error!("command :history, objects not found!")
|
49
|
+
end
|
50
|
+
say render_history(res)
|
51
|
+
say
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "load [CONF, ...]", "load config (run eye-daemon if not) (-f foreground load)"
|
55
|
+
method_option :foreground, :type => :boolean, :aliases => "-f"
|
56
|
+
def load(*configs)
|
57
|
+
configs.map!{ |c| File.expand_path(c) } if !configs.empty?
|
58
|
+
|
59
|
+
if options[:foreground]
|
60
|
+
# in foreground we stop another server, and run just 1 current config version
|
61
|
+
error!("foreground expected only one config") if configs.size > 1
|
62
|
+
server_start_foreground(configs.first)
|
63
|
+
|
64
|
+
elsif server_started?
|
65
|
+
say_load_result cmd(:load, *configs)
|
66
|
+
|
67
|
+
else
|
68
|
+
server_start(configs)
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
desc "quit", "eye-daemon quit"
|
74
|
+
method_option :stop_all, :type => :boolean, :aliases => "-s"
|
75
|
+
method_option :timeout, :type => :string, :aliases => "-t", :default => "600"
|
76
|
+
def quit
|
77
|
+
if options[:stop_all]
|
78
|
+
Eye::Local.client_timeout = options[:timeout].to_i
|
79
|
+
cmd(:stop_all, options[:timeout].to_i)
|
80
|
+
end
|
81
|
+
|
82
|
+
Eye::Local.client_timeout = 5
|
83
|
+
res = _cmd(:quit)
|
84
|
+
|
85
|
+
# if eye server got crazy, stop by force
|
86
|
+
ensure_stop_previous_server if res != :corrupted_data
|
87
|
+
|
88
|
+
# remove pid_file
|
89
|
+
File.delete(Eye::Local.pid_path) if File.exists?(Eye::Local.pid_path)
|
90
|
+
|
91
|
+
say "Quit :(", :yellow
|
92
|
+
end
|
93
|
+
|
94
|
+
[:start, :stop, :restart, :unmonitor, :monitor, :delete, :match].each do |_cmd|
|
95
|
+
desc "#{_cmd} MASK[,...]", "#{_cmd} app,group or process"
|
96
|
+
define_method(_cmd) do |*masks|
|
97
|
+
send_command(_cmd, *masks)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
desc "signal SIG MASK[,...]", "send signal to app,group or process"
|
102
|
+
def signal(sig, *masks)
|
103
|
+
send_command(:signal, sig, *masks)
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "break MASK[,...]", "break chain executing"
|
107
|
+
def break(*masks)
|
108
|
+
send_command(:break_chain, *masks)
|
109
|
+
end
|
110
|
+
|
111
|
+
desc "trace [MASK]", "tracing log(tail + grep) for app,group or process"
|
112
|
+
def trace(mask = "")
|
113
|
+
log_trace(mask)
|
114
|
+
end
|
115
|
+
|
116
|
+
map ["-v", "--version"] => :version
|
117
|
+
desc "version", "version"
|
118
|
+
def version
|
119
|
+
say Eye::ABOUT
|
120
|
+
end
|
121
|
+
|
122
|
+
desc "check CONF", "check config file syntax"
|
123
|
+
method_option :host, :type => :string, :aliases => "-h"
|
124
|
+
method_option :verbose, :type => :boolean, :aliases => "-v"
|
125
|
+
def check(conf)
|
126
|
+
conf = File.expand_path(conf) if conf && !conf.empty?
|
127
|
+
|
128
|
+
Eye::Local.host = options[:host] if options[:host]
|
129
|
+
Eye::Dsl.verbose = options[:verbose]
|
130
|
+
|
131
|
+
say_load_result Eye::Controller.new.check(conf), :syntax => true
|
132
|
+
end
|
133
|
+
|
134
|
+
desc "explain CONF", "explain config tree"
|
135
|
+
method_option :host, :type => :string, :aliases => "-h"
|
136
|
+
method_option :verbose, :type => :boolean, :aliases => "-v"
|
137
|
+
def explain(conf)
|
138
|
+
conf = File.expand_path(conf) if conf && !conf.empty?
|
139
|
+
|
140
|
+
Eye::Local.host = options[:host] if options[:host]
|
141
|
+
Eye::Dsl.verbose = options[:verbose]
|
142
|
+
|
143
|
+
say_load_result Eye::Controller.new.explain(conf), :print_config => true, :syntax => true
|
144
|
+
end
|
145
|
+
|
146
|
+
desc "watch [MASK]", "interactive processes info"
|
147
|
+
def watch(*args)
|
148
|
+
error!("You should install watch utility") if `which watch`.empty?
|
149
|
+
|
150
|
+
cmd = if `watch --version 2>&1`.chop > '0.2.0'
|
151
|
+
"watch -n 1 --color #{$0} i #{args * ' '}"
|
152
|
+
else
|
153
|
+
"watch -n 1 #{$0} i #{args * ' '}"
|
154
|
+
end
|
155
|
+
|
156
|
+
pid = Process.spawn(cmd)
|
157
|
+
Process.waitpid(pid)
|
158
|
+
rescue Interrupt
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def error!(msg)
|
164
|
+
say msg, :red
|
165
|
+
exit 1
|
166
|
+
end
|
167
|
+
|
168
|
+
def print(msg, new_line = true)
|
169
|
+
say msg if msg && !msg.empty?
|
170
|
+
say if new_line
|
171
|
+
end
|
172
|
+
|
173
|
+
def log_trace(tag = '')
|
174
|
+
log_file = cmd(:logger_dev)
|
175
|
+
if log_file && File.exists?(log_file)
|
176
|
+
Process.exec "tail -n 100 -f #{log_file} | grep '#{tag}'"
|
177
|
+
else
|
178
|
+
error! "log file not found #{log_file.inspect}"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.exit_on_failure?
|
183
|
+
true
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Eye::Cli::Commands
|
2
|
+
private
|
3
|
+
|
4
|
+
def client
|
5
|
+
@client ||= Eye::Client.new(Eye::Local.socket_path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def _cmd(cmd, *args)
|
9
|
+
client.command(cmd, *args)
|
10
|
+
rescue Errno::ECONNREFUSED, Errno::ENOENT
|
11
|
+
:not_started
|
12
|
+
end
|
13
|
+
|
14
|
+
def cmd(cmd, *args)
|
15
|
+
res = _cmd(cmd, *args)
|
16
|
+
|
17
|
+
if res == :not_started
|
18
|
+
error! "socket(#{Eye::Local.socket_path}) not found, did you run `eye load`?"
|
19
|
+
elsif res == :timeouted
|
20
|
+
error! 'eye timed out without responding...'
|
21
|
+
end
|
22
|
+
|
23
|
+
res
|
24
|
+
end
|
25
|
+
|
26
|
+
def say_load_result(res = {}, opts = {})
|
27
|
+
error!(res) unless res.is_a?(Hash)
|
28
|
+
say_filename = (res.size > 1)
|
29
|
+
error = false
|
30
|
+
res.each do |filename, _res|
|
31
|
+
say "#{filename}: ", nil, true if say_filename
|
32
|
+
show_load_message(_res, opts)
|
33
|
+
error = true if _res[:error]
|
34
|
+
end
|
35
|
+
|
36
|
+
exit(1) if error
|
37
|
+
end
|
38
|
+
|
39
|
+
def show_load_message(res, opts = {})
|
40
|
+
if res[:error]
|
41
|
+
say res[:message], :red
|
42
|
+
res[:backtrace].to_a.each{|line| say line, :red }
|
43
|
+
else
|
44
|
+
if opts[:syntax]
|
45
|
+
say 'Config ok!', :green if !res[:empty]
|
46
|
+
else
|
47
|
+
say 'Config loaded!', :green if !res[:empty]
|
48
|
+
end
|
49
|
+
|
50
|
+
if opts[:print_config]
|
51
|
+
require 'pp'
|
52
|
+
PP.pp res[:config], STDOUT, 150
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_command(_cmd, *args)
|
58
|
+
res = cmd(_cmd, *args)
|
59
|
+
if res == :unknown_command
|
60
|
+
error! "unknown command :#{_cmd}"
|
61
|
+
elsif res == :corrupted_data
|
62
|
+
error! 'something crazy wrong, check eye logs!'
|
63
|
+
elsif res.is_a?(Hash)
|
64
|
+
if res[:error]
|
65
|
+
error! "Error: #{res[:error]}"
|
66
|
+
elsif res = res[:result]
|
67
|
+
if res == []
|
68
|
+
error! "command :#{_cmd}, objects not found!"
|
69
|
+
else
|
70
|
+
say "command :#{_cmd} sent to [#{res * ", "}]"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
else
|
74
|
+
error! "unknown result #{res.inspect}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Eye::Cli::Render
|
2
|
+
private
|
3
|
+
def render_info(data)
|
4
|
+
error!("unexpected server response #{data.inspect}") unless data.is_a?(Hash)
|
5
|
+
|
6
|
+
make_str data
|
7
|
+
end
|
8
|
+
|
9
|
+
def make_str(data, level = -1)
|
10
|
+
return nil if !data || data.empty?
|
11
|
+
|
12
|
+
if data.is_a?(Array)
|
13
|
+
data.map{|el| make_str(el, level) }.compact * "\n"
|
14
|
+
else
|
15
|
+
str = nil
|
16
|
+
|
17
|
+
if data[:name]
|
18
|
+
return make_str(data[:subtree], level) if data[:name] == '__default__'
|
19
|
+
|
20
|
+
off = level * 2
|
21
|
+
off_str = ' ' * off
|
22
|
+
|
23
|
+
short_state = (data[:type] == :application && data[:states])
|
24
|
+
is_text = data[:state] || data[:states]
|
25
|
+
|
26
|
+
name = (data[:type] == :application && !is_text) ? "\033[1m#{data[:name]}\033[0m" : data[:name].to_s
|
27
|
+
off_len = 35
|
28
|
+
str = off_str + (name + ' ').ljust(off_len - off, is_text ? '.' : ' ')
|
29
|
+
|
30
|
+
if short_state
|
31
|
+
str += ' ' + data[:states].map { |k, v| "#{k}:#{v}" }.join(', ')
|
32
|
+
elsif data[:state]
|
33
|
+
str += ' ' + data[:state].to_s
|
34
|
+
str += ' (' + resources_str(data[:resources]) + ')' if data[:resources] && data[:state].to_sym == :up
|
35
|
+
str += " (#{data[:state_reason]} at #{Eye::Utils.human_time2(data[:state_changed_at])})" if data[:state_reason] && data[:state] == 'unmonitored'
|
36
|
+
elsif data[:current_command]
|
37
|
+
chain_progress = if data[:chain_progress]
|
38
|
+
" #{data[:chain_progress][0]} of #{data[:chain_progress][1]}" rescue ''
|
39
|
+
end
|
40
|
+
str += " \e[1;33m[#{data[:current_command]}#{chain_progress}]\033[0m"
|
41
|
+
str += " (#{data[:chain_commands] * ', '})" if data[:chain_commands]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
if data[:subtree].nil?
|
47
|
+
str
|
48
|
+
elsif !data[:subtree] && data[:type] != :application
|
49
|
+
nil
|
50
|
+
else
|
51
|
+
[str, make_str(data[:subtree], level + 1)].compact * "\n"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def resources_str(r)
|
57
|
+
return '' if !r || r.empty?
|
58
|
+
memory, cpu, start_time, pid = r[:memory], r[:cpu], r[:start_time], r[:pid]
|
59
|
+
return '' unless memory && cpu && start_time
|
60
|
+
|
61
|
+
"#{Eye::Utils.human_time(start_time)}, #{cpu.to_i}%, #{memory / 1024 / 1024}Mb, <#{pid}>"
|
62
|
+
end
|
63
|
+
|
64
|
+
def render_debug_info(data)
|
65
|
+
error!("unexpected server response #{data.inspect}") unless data.is_a?(Hash)
|
66
|
+
|
67
|
+
s = ""
|
68
|
+
|
69
|
+
if config_yaml = data.delete(:config_yaml)
|
70
|
+
s << config_yaml
|
71
|
+
|
72
|
+
else
|
73
|
+
data.each do |k, v|
|
74
|
+
s << "#{"#{k}:".ljust(10)} "
|
75
|
+
|
76
|
+
case k
|
77
|
+
when :resources
|
78
|
+
s << resources_str(v)
|
79
|
+
else
|
80
|
+
s << "#{v}"
|
81
|
+
end
|
82
|
+
|
83
|
+
s << "\n"
|
84
|
+
end
|
85
|
+
|
86
|
+
s << "\n"
|
87
|
+
end
|
88
|
+
|
89
|
+
s
|
90
|
+
end
|
91
|
+
|
92
|
+
def render_history(data)
|
93
|
+
error!("unexpected server response #{data.inspect}") unless data.is_a?(Hash)
|
94
|
+
|
95
|
+
res = []
|
96
|
+
data.each do |name, data|
|
97
|
+
res << detail_process_info(name, data)
|
98
|
+
end
|
99
|
+
|
100
|
+
res * "\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
def detail_process_info(name, history)
|
104
|
+
return if history.empty?
|
105
|
+
|
106
|
+
res = "\033[1m#{name}\033[0m\n"
|
107
|
+
history = history.reverse
|
108
|
+
|
109
|
+
history.chunk{|h| [h[:state], h[:reason].to_s] }.each do |_, hist|
|
110
|
+
if hist.size >= 3
|
111
|
+
res << detail_process_info_string(hist[0])
|
112
|
+
res << detail_process_info_string(:state => "... #{hist.size - 2} times", :reason => '...')
|
113
|
+
res << detail_process_info_string(hist[-1])
|
114
|
+
else
|
115
|
+
hist.each do |h|
|
116
|
+
res << detail_process_info_string(h)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
res
|
122
|
+
end
|
123
|
+
|
124
|
+
def detail_process_info_string(h)
|
125
|
+
state = h[:state].to_s.ljust(14)
|
126
|
+
at = h[:at] ? Eye::Utils.human_time2(h[:at]) : '.' * 12
|
127
|
+
"#{at} - #{state} (#{h[:reason]})\n"
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|