ace-eye 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGES.md +77 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE +22 -0
  8. data/README.md +212 -0
  9. data/Rakefile +35 -0
  10. data/bin/eye +5 -0
  11. data/bin/loader_eye +72 -0
  12. data/bin/runner +16 -0
  13. data/examples/dependency.eye +17 -0
  14. data/examples/notify.eye +19 -0
  15. data/examples/plugin/README.md +15 -0
  16. data/examples/plugin/main.eye +15 -0
  17. data/examples/plugin/plugin.rb +63 -0
  18. data/examples/process_thin.rb +29 -0
  19. data/examples/processes/em.rb +57 -0
  20. data/examples/processes/forking.rb +20 -0
  21. data/examples/processes/sample.rb +144 -0
  22. data/examples/processes/thin.ru +12 -0
  23. data/examples/puma.eye +29 -0
  24. data/examples/rbenv.eye +11 -0
  25. data/examples/sidekiq.eye +23 -0
  26. data/examples/test.eye +87 -0
  27. data/examples/thin-farm.eye +30 -0
  28. data/examples/unicorn.eye +39 -0
  29. data/eye.gemspec +40 -0
  30. data/lib/eye.rb +28 -0
  31. data/lib/eye/application.rb +73 -0
  32. data/lib/eye/checker.rb +258 -0
  33. data/lib/eye/checker/children_count.rb +44 -0
  34. data/lib/eye/checker/children_memory.rb +12 -0
  35. data/lib/eye/checker/cpu.rb +17 -0
  36. data/lib/eye/checker/cputime.rb +13 -0
  37. data/lib/eye/checker/file_ctime.rb +24 -0
  38. data/lib/eye/checker/file_size.rb +34 -0
  39. data/lib/eye/checker/file_touched.rb +15 -0
  40. data/lib/eye/checker/http.rb +96 -0
  41. data/lib/eye/checker/memory.rb +17 -0
  42. data/lib/eye/checker/nop.rb +6 -0
  43. data/lib/eye/checker/runtime.rb +18 -0
  44. data/lib/eye/checker/socket.rb +159 -0
  45. data/lib/eye/child_process.rb +101 -0
  46. data/lib/eye/cli.rb +185 -0
  47. data/lib/eye/cli/commands.rb +78 -0
  48. data/lib/eye/cli/render.rb +130 -0
  49. data/lib/eye/cli/server.rb +93 -0
  50. data/lib/eye/client.rb +32 -0
  51. data/lib/eye/config.rb +91 -0
  52. data/lib/eye/control.rb +2 -0
  53. data/lib/eye/controller.rb +54 -0
  54. data/lib/eye/controller/commands.rb +88 -0
  55. data/lib/eye/controller/helpers.rb +101 -0
  56. data/lib/eye/controller/load.rb +224 -0
  57. data/lib/eye/controller/options.rb +18 -0
  58. data/lib/eye/controller/send_command.rb +177 -0
  59. data/lib/eye/controller/status.rb +72 -0
  60. data/lib/eye/dsl.rb +53 -0
  61. data/lib/eye/dsl/application_opts.rb +39 -0
  62. data/lib/eye/dsl/chain.rb +12 -0
  63. data/lib/eye/dsl/child_process_opts.rb +13 -0
  64. data/lib/eye/dsl/config_opts.rb +55 -0
  65. data/lib/eye/dsl/group_opts.rb +32 -0
  66. data/lib/eye/dsl/helpers.rb +20 -0
  67. data/lib/eye/dsl/main.rb +51 -0
  68. data/lib/eye/dsl/opts.rb +151 -0
  69. data/lib/eye/dsl/process_opts.rb +36 -0
  70. data/lib/eye/dsl/pure_opts.rb +121 -0
  71. data/lib/eye/dsl/validation.rb +88 -0
  72. data/lib/eye/group.rb +140 -0
  73. data/lib/eye/group/chain.rb +81 -0
  74. data/lib/eye/loader.rb +10 -0
  75. data/lib/eye/local.rb +100 -0
  76. data/lib/eye/logger.rb +104 -0
  77. data/lib/eye/notify.rb +118 -0
  78. data/lib/eye/notify/jabber.rb +30 -0
  79. data/lib/eye/notify/mail.rb +48 -0
  80. data/lib/eye/process.rb +85 -0
  81. data/lib/eye/process/children.rb +60 -0
  82. data/lib/eye/process/commands.rb +280 -0
  83. data/lib/eye/process/config.rb +81 -0
  84. data/lib/eye/process/controller.rb +73 -0
  85. data/lib/eye/process/data.rb +78 -0
  86. data/lib/eye/process/monitor.rb +108 -0
  87. data/lib/eye/process/notify.rb +32 -0
  88. data/lib/eye/process/scheduler.rb +82 -0
  89. data/lib/eye/process/states.rb +86 -0
  90. data/lib/eye/process/states_history.rb +66 -0
  91. data/lib/eye/process/system.rb +97 -0
  92. data/lib/eye/process/trigger.rb +34 -0
  93. data/lib/eye/process/validate.rb +33 -0
  94. data/lib/eye/process/watchers.rb +66 -0
  95. data/lib/eye/reason.rb +20 -0
  96. data/lib/eye/server.rb +60 -0
  97. data/lib/eye/sigar.rb +5 -0
  98. data/lib/eye/system.rb +139 -0
  99. data/lib/eye/system_resources.rb +99 -0
  100. data/lib/eye/trigger.rb +136 -0
  101. data/lib/eye/trigger/check_dependency.rb +30 -0
  102. data/lib/eye/trigger/flapping.rb +41 -0
  103. data/lib/eye/trigger/stop_children.rb +17 -0
  104. data/lib/eye/trigger/transition.rb +15 -0
  105. data/lib/eye/trigger/wait_dependency.rb +49 -0
  106. data/lib/eye/utils.rb +45 -0
  107. data/lib/eye/utils/alive_array.rb +57 -0
  108. data/lib/eye/utils/celluloid_chain.rb +71 -0
  109. data/lib/eye/utils/celluloid_klass.rb +5 -0
  110. data/lib/eye/utils/leak_19.rb +10 -0
  111. data/lib/eye/utils/mini_active_support.rb +111 -0
  112. data/lib/eye/utils/pmap.rb +7 -0
  113. data/lib/eye/utils/tail.rb +20 -0
  114. metadata +398 -0
@@ -0,0 +1,121 @@
1
+ class Eye::Dsl::PureOpts
2
+
3
+ def self.create_options_methods(arr, types = nil)
4
+ m = Module.new do
5
+ arr.each do |opt|
6
+ define_method("set_#{opt}") do |arg|
7
+ key = opt.to_sym
8
+
9
+ if (disallow_options && disallow_options.include?(key)) || (allow_options && !allow_options.include?(key))
10
+ raise Eye::Dsl::Error, "disallow option #{key} for #{self.class.inspect}"
11
+ end
12
+
13
+ if types
14
+ good_type = Array(types).any?{|type| arg.is_a?(type) } || arg.nil?
15
+ raise Eye::Dsl::Error, "bad :#{opt} value #{arg.inspect}, type should be #{types.inspect}" unless good_type
16
+ end
17
+
18
+ @config[key] = arg
19
+ end
20
+
21
+ define_method("get_#{opt}") do
22
+ @config[ opt.to_sym ]
23
+ end
24
+
25
+ define_method(opt) do |*args|
26
+ if args.blank?
27
+ # getter
28
+ send "get_#{opt}"
29
+ else
30
+ send "set_#{opt}", *args
31
+ end
32
+ end
33
+
34
+ define_method("#{opt}=") do |arg|
35
+ send opt, arg
36
+ end
37
+ end
38
+ end
39
+
40
+ self.send :include, m
41
+ end
42
+
43
+ attr_reader :name, :full_name
44
+ attr_reader :config, :parent
45
+
46
+ def initialize(name = nil, parent = nil, merge_parent_config = true)
47
+ @name = name.to_s
48
+ @full_name = @name
49
+
50
+ if parent
51
+ @parent = parent
52
+ if merge_parent_config
53
+ @config = Eye::Utils::deep_clone(parent.config)
54
+ parent.not_seed_options.each { |opt| @config.delete(opt) }
55
+ else
56
+ @config = {}
57
+ end
58
+ @full_name = "#{parent.full_name}:#{@full_name}"
59
+ else
60
+ @config = {}
61
+ end
62
+
63
+ @config[:name] = @name if @name.present?
64
+ end
65
+
66
+ def allow_options
67
+ nil
68
+ end
69
+
70
+ def disallow_options
71
+ []
72
+ end
73
+
74
+ def not_seed_options
75
+ []
76
+ end
77
+
78
+ def with_condition(cond = true, &block)
79
+ self.instance_eval(&block) if cond && block
80
+ end
81
+
82
+ def use(proc, *args)
83
+ if proc.is_a?(String)
84
+ self.class.with_parsed_file(proc) do |path|
85
+ if File.exists?(path)
86
+ Eye::Dsl.debug "=> load #{path}"
87
+ self.instance_eval(File.read(path))
88
+ Eye::Dsl.debug "<= load #{path}"
89
+ end
90
+ end
91
+ else
92
+ ie = if args.present?
93
+ lambda{|i| proc[i, *args] }
94
+ else
95
+ proc
96
+ end
97
+
98
+ self.instance_eval(&ie)
99
+ end
100
+ end
101
+
102
+ def nop(*args, &block); end
103
+
104
+ private
105
+
106
+ def self.with_parsed_file(file_name)
107
+ saved_parsed_filename = Eye.parsed_filename
108
+
109
+ require 'pathname'
110
+
111
+ real_filename = Eye.parsed_filename && File.symlink?(Eye.parsed_filename) ? File.readlink(Eye.parsed_filename) : Eye.parsed_filename
112
+ dirname = File.dirname(real_filename) rescue nil
113
+ path = Pathname.new(file_name).expand_path(dirname).to_s
114
+
115
+ Eye.parsed_filename = path
116
+ yield path
117
+ ensure
118
+ Eye.parsed_filename = saved_parsed_filename
119
+ end
120
+
121
+ end
@@ -0,0 +1,88 @@
1
+ module Eye::Dsl::Validation
2
+ def self.included(base)
3
+ base.extend(ClassMethods)
4
+ end
5
+
6
+ class Error < Exception; end
7
+
8
+ module ClassMethods
9
+ def inherited(subclass)
10
+ subclass.validates = self.validates.clone
11
+ subclass.should_bes = self.should_bes.clone
12
+ subclass.defaults = self.defaults.clone
13
+ subclass.variants = self.variants.clone
14
+ end
15
+
16
+ attr_accessor :validates, :should_bes, :defaults, :variants
17
+
18
+ def validates; @validates ||= {}; end
19
+ def should_bes; @should_bes ||= []; end
20
+ def defaults; @defaults ||= {}; end
21
+ def variants; @variants ||= {}; end
22
+
23
+ def param(param, types = [], should_be = false, default = nil, _variants = nil)
24
+ param = param.to_sym
25
+
26
+ validates[param] = types
27
+ should_bes << param if should_be
28
+ param_default(param, default)
29
+ variants[param] = _variants
30
+
31
+ return if param == :do
32
+
33
+ define_method "#{param}" do
34
+ value = @options[param]
35
+ value.nil? ? default : value
36
+ end
37
+ end
38
+
39
+ def param_default(param, default)
40
+ param = param.to_sym
41
+ defaults[param] = default
42
+ end
43
+
44
+ def del_param(param)
45
+ param = param.to_sym
46
+ validates.delete(param)
47
+ should_bes.delete(param)
48
+ defaults.delete(param)
49
+ variants.delete(param)
50
+ remove_method(param)
51
+ end
52
+
53
+ def validate(options = {})
54
+ options.each do |param, value|
55
+ param = param.to_sym
56
+ types = validates[param]
57
+ unless types
58
+ if param != :type
59
+ raise Error, "#{self.name} unknown param :#{param} value #{value.inspect}"
60
+ end
61
+ end
62
+
63
+ if self.variants[param]
64
+ if value && !value.is_a?(Proc)
65
+ if value.is_a?(Array)
66
+ if (value - self.variants[param]).present?
67
+ raise Error, "#{value.inspect} should be within #{self.variants[param].inspect}"
68
+ end
69
+ elsif !self.variants[param].include?(value)
70
+ raise Error, "#{value.inspect} should be within #{self.variants[param].inspect}"
71
+ end
72
+ end
73
+ end
74
+
75
+ next if types.blank?
76
+
77
+ types = Array(types)
78
+ good = types.any?{|type| value.is_a?(type) }
79
+ raise Error, "#{self.name} bad param :#{param} value #{value.inspect}, type #{types.inspect}" unless good
80
+ end
81
+
82
+ should_bes.each do |param|
83
+ raise Error, "#{self.name} for param :#{param} value should be" unless options[param.to_sym] || defaults[param.to_sym]
84
+ end
85
+ end
86
+ end
87
+
88
+ end
@@ -0,0 +1,140 @@
1
+ require 'celluloid'
2
+
3
+ class Eye::Group
4
+ include Celluloid
5
+
6
+ autoload :Chain, 'eye/group/chain'
7
+
8
+ include Eye::Process::Scheduler
9
+ include Eye::Group::Chain
10
+
11
+ attr_reader :processes, :name, :hidden, :config
12
+
13
+ def initialize(name, config)
14
+ @name = name
15
+ @config = config
16
+ @processes = Eye::Utils::AliveArray.new
17
+ @hidden = (name == '__default__')
18
+ debug 'created'
19
+ end
20
+
21
+ def logger_tag
22
+ full_name
23
+ end
24
+
25
+ def app_name
26
+ @config[:application]
27
+ end
28
+
29
+ def full_name
30
+ @full_name ||= "#{app_name}:#{@name}"
31
+ end
32
+
33
+ def update_config(cfg)
34
+ @config = cfg
35
+ @full_name = nil
36
+ end
37
+
38
+ def add_process(process)
39
+ @processes << process
40
+ end
41
+
42
+ # sort processes in name order
43
+ def resort_processes
44
+ @processes = @processes.sort_by(&:name)
45
+ end
46
+
47
+ def status_data(debug = false)
48
+ plist = @processes.map{|p| p.status_data(debug) }
49
+
50
+ h = { name: name, type: :group, subtree: plist }
51
+
52
+ h.merge!(debug: debug_data) if debug
53
+
54
+ # show current chain
55
+ if current_scheduled_command
56
+ h.update(current_command: current_scheduled_command)
57
+
58
+ if (chain_commands = scheduler_actions_list) && chain_commands.present?
59
+ h.update(chain_commands: chain_commands)
60
+ end
61
+
62
+ if @chain_processes_current && @chain_processes_count
63
+ h.update(chain_progress: [@chain_processes_current, @chain_processes_count])
64
+ end
65
+ end
66
+
67
+ h
68
+ end
69
+
70
+ def debug_data
71
+ {:queue => scheduler_actions_list, :chain => chain_status}
72
+ end
73
+
74
+ def send_command(command, *args)
75
+ info "send_command: #{command}"
76
+
77
+ case command
78
+ when :delete
79
+ delete *args
80
+ when :break_chain
81
+ break_chain *args
82
+ else
83
+ schedule command, *args, Eye::Reason::User.new(command)
84
+ end
85
+ end
86
+
87
+ def start
88
+ chain_command :start
89
+ end
90
+
91
+ def stop
92
+ async_schedule :stop
93
+ end
94
+
95
+ def restart
96
+ chain_command :restart
97
+ end
98
+
99
+ def delete
100
+ async_schedule :delete
101
+ terminate
102
+ end
103
+
104
+ def monitor
105
+ chain_command :monitor
106
+ end
107
+
108
+ def unmonitor
109
+ async_schedule :unmonitor
110
+ end
111
+
112
+ def signal(sig)
113
+ async_schedule :signal, sig
114
+ end
115
+
116
+ def break_chain
117
+ info 'break chain'
118
+ scheduler_clear_pending_list
119
+ @chain_breaker = true
120
+ end
121
+
122
+ def clear
123
+ @processes = Eye::Utils::AliveArray.new
124
+ end
125
+
126
+ def sub_object?(obj)
127
+ @processes.include?(obj)
128
+ end
129
+
130
+ private
131
+
132
+ def async_schedule(command, *args)
133
+ info "send to all processes #{command} #{args.present? ? args*',' : nil}"
134
+
135
+ @processes.each do |process|
136
+ process.send_command(command, *args)
137
+ end
138
+ end
139
+
140
+ end
@@ -0,0 +1,81 @@
1
+ module Eye::Group::Chain
2
+
3
+ private
4
+
5
+ def chain_schedule(type, grace, command, *args)
6
+ info "starting #{type} with #{grace}s chain #{command} #{args}"
7
+
8
+ @chain_processes_count = @processes.size
9
+ @chain_processes_current = 0
10
+ @chain_breaker = false
11
+
12
+ started_at = Time.now
13
+
14
+ @processes.each do | process |
15
+ chain_schedule_process(process, type, command, *args)
16
+
17
+ @chain_processes_current = @chain_processes_current.to_i + 1
18
+
19
+ # to skip last sleep
20
+ break if @chain_processes_current.to_i == @chain_processes_count.to_i
21
+ break if @chain_breaker
22
+
23
+ # wait next process
24
+ sleep grace.to_f
25
+
26
+ break if @chain_breaker
27
+ end
28
+
29
+ debug "chain finished #{Time.now - started_at}s"
30
+
31
+ @chain_processes_count = nil
32
+ @chain_processes_current = nil
33
+ end
34
+
35
+ def chain_schedule_process(process, type, command, *args)
36
+ debug "chain_schedule_process #{process.name} #{type} #{command}"
37
+
38
+ if type == :sync
39
+ # sync command, with waiting
40
+ # this is very hackety, because call method of the process without its scheduler
41
+ # need to provide some scheduler future
42
+ process.last_scheduled_reason = self.last_scheduled_reason
43
+ process.send(command, *args)
44
+ else
45
+ # async command
46
+ process.send_command(command, *args)
47
+ end
48
+ end
49
+
50
+ def chain_status
51
+ if @config[:chain]
52
+ [:start, :restart].map{|c| @config[:chain][c].try(:[], :grace) }
53
+ end
54
+ end
55
+
56
+ def chain_command(command, *args)
57
+ chain_opts = chain_options(command)
58
+ chain_schedule(chain_opts[:type], chain_opts[:grace], command, *args)
59
+ end
60
+
61
+ # with such delay will chained processes by default
62
+ DEFAULT_CHAIN = 0.2
63
+
64
+ def chain_options(command)
65
+ command = :start if command == :monitor # hack for monitor command, work as start
66
+
67
+ if @config[:chain] && @config[:chain][command]
68
+ type = @config[:chain][command].try :[], :type
69
+ type = [:async, :sync].include?(type) ? type : :async
70
+
71
+ grace = @config[:chain][command].try :[], :grace
72
+ grace = grace ? (grace.to_f rescue DEFAULT_CHAIN) : DEFAULT_CHAIN
73
+
74
+ {:type => type, :grace => grace}
75
+ else
76
+ # default chain case
77
+ {:type => :async, :grace => DEFAULT_CHAIN}
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,10 @@
1
+ # add gems to $: by `gem` method
2
+ # this is only way when install eye as system wide
3
+
4
+ gem 'celluloid', '~> 0.15.0'
5
+ gem 'celluloid-io', '~> 0.15.0'
6
+ gem 'nio4r'
7
+ gem 'timers'
8
+
9
+ gem 'state_machine'
10
+ gem 'sigar', '~> 0.7.2'
@@ -0,0 +1,100 @@
1
+ require 'fileutils'
2
+
3
+ module Eye::Local
4
+ class << self
5
+ def dir
6
+ @dir ||= begin
7
+ if root?
8
+ '/var/run/eye'
9
+ else
10
+ File.expand_path(File.join(home, '.eye'))
11
+ end
12
+ end
13
+ end
14
+
15
+ def dir=(d)
16
+ @dir = d
17
+ end
18
+
19
+ def eyeconfig
20
+ if root?
21
+ '/etc/eye.conf'
22
+ else
23
+ File.expand_path(File.join(home, '.eyeconfig'))
24
+ end
25
+ end
26
+
27
+ def root?
28
+ Process::UID.eid == 0
29
+ end
30
+
31
+ def home
32
+ h = ENV['EYE_HOME'] || ENV['HOME']
33
+ raise "HOME undefined, should be HOME or EYE_HOME environment" unless h
34
+ h
35
+ end
36
+
37
+ def path(path)
38
+ File.join(dir, path)
39
+ end
40
+
41
+ def ensure_eye_dir
42
+ FileUtils.mkdir_p( dir )
43
+ end
44
+
45
+ def socket_path
46
+ path(ENV['EYE_SOCK'] || "sock#{ENV['EYE_V']}")
47
+ end
48
+
49
+ def pid_path
50
+ path(ENV['EYE_PID'] || "pid#{ENV['EYE_V']}")
51
+ end
52
+
53
+ def cache_path
54
+ path("processes#{ENV['EYE_V']}.cache")
55
+ end
56
+
57
+ def client_timeout
58
+ @client_timeout ||= 5
59
+ end
60
+
61
+ def client_timeout=(cl)
62
+ @client_timeout = cl
63
+ end
64
+
65
+ def supported_setsid?
66
+ RUBY_VERSION >= '2.0'
67
+ end
68
+
69
+ def host
70
+ @host ||= begin
71
+ require 'socket'
72
+ Socket.gethostname
73
+ end
74
+ end
75
+
76
+ def host=(hostname)
77
+ @host = hostname
78
+ end
79
+
80
+ def eyefile
81
+ @eyefile ||= find_eyefile('.')
82
+ end
83
+
84
+ def find_eyefile(start_from_dir)
85
+ fromenv = ENV['EYEFILE']
86
+ return fromenv if fromenv && !fromenv.empty? && File.exist?(fromenv)
87
+
88
+ previous = nil
89
+ current = File.expand_path(start_from_dir)
90
+
91
+ until !File.directory?(current) || current == previous
92
+ filename = File.join(current, 'Eyefile')
93
+ return filename if File.file?(filename)
94
+ current, previous = File.expand_path('..', current), current
95
+ end
96
+ end
97
+
98
+ attr_accessor :local_runner
99
+ end
100
+ end