honcho 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 21631e6c9c4f232d6dcdde64454ebfac347112e9
4
- data.tar.gz: 46eb594180d364e2dc5e3576a9e7e293fc7da92b
3
+ metadata.gz: 45989ba8f3704c6d5172f9518e7bfc1c2201d6db
4
+ data.tar.gz: 9e8f3b779d67d975a8ccdb7f30ba30ab933c04da
5
5
  SHA512:
6
- metadata.gz: e365db119de34c395d23bd1e58e47c94aba937bd6c7cd7753fc783f3bf4317ce85b1f4d95f06ea2e1c8fc75d9ed2bc9254828c83726011f3a6758adc2f60bcc0
7
- data.tar.gz: d97ae928be10369b2870049379189c244eaf4e1f8721a76c401a2f0203ac0cd296698e0964d7f19fecd818398ec75034c779adeac5cc866c18a07ba0f567d1a9
6
+ metadata.gz: b3ad82f616bc1b8deeeff75a498889f5c7804d20145a58e2dd4b1964dfa4d69bda29efd3773f5d13f92184751736d285abf597bc0095707bbb7fe4927926a62f
7
+ data.tar.gz: d541b3ad3601e544e244ef2e1ad1a32bf02e9c892dba2b32f7ea7a09695358e8da50a272fbe58d6f6eab77096177b2e81e96b4d868f1acb9b2f259023d889e03
data/bin/honcho CHANGED
@@ -1,5 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'optparse'
3
4
  require_relative '../lib/honcho'
4
5
 
5
- Honcho.new.run
6
+ options = {
7
+ config: 'honcho.yml'
8
+ }
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = 'Usage: honcho [options]'
12
+
13
+ opts.on('-c', '--config path', 'specify config file path') do |path|
14
+ options[:config] = path
15
+ end
16
+ end.parse!
17
+
18
+ Honcho::Runner.new(options).run
@@ -1,178 +1 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'redis'
4
- require 'time'
5
- require 'stringio'
6
- require 'yaml'
7
- require_relative './honcho/adapters'
8
-
9
- Thread.abort_on_exception = true
10
-
11
- class Honcho
12
- COLORS = {
13
- red: 31,
14
- green: 32,
15
- yellow: 33,
16
- blue: 34,
17
- magenta: 35,
18
- cyan: 36,
19
- bright_red: 31,
20
- bright_green: 32,
21
- bright_yellow: 33,
22
- bright_blue: 34,
23
- bright_magenta: 35,
24
- bright_cyan: 36
25
- }.freeze
26
-
27
- def initialize
28
- @running = {}
29
- @stopping = {}
30
- @redis = Redis.new
31
- @adapters = build_adapters
32
- @colors = assign_colors
33
- end
34
-
35
- attr_reader :adapters, :running, :stopping, :redis, :colors
36
-
37
- def run
38
- trap(:INT) { term_all && exit }
39
- trap(:TERM) { term_all && exit }
40
- loop do
41
- check_for_work
42
- sleep(interval)
43
- end
44
- end
45
-
46
- private
47
-
48
- def apps
49
- config['apps']
50
- end
51
-
52
- def command_template
53
- config['command_template'] || '%s'
54
- end
55
-
56
- def stop_delay
57
- config['stop_delay'] || 30
58
- end
59
-
60
- def interval
61
- config['interval'] || 2
62
- end
63
-
64
- def config
65
- @config ||= YAML.load_file('honcho.yml')
66
- end
67
-
68
- def check_for_work
69
- adapters.each do |adapter|
70
- if adapter.run?
71
- start(adapter)
72
- else
73
- stop(adapter)
74
- end
75
- end
76
- end
77
-
78
- def start(adapter)
79
- stopping.delete(adapter)
80
- return if running[adapter]
81
- log(adapter.config['name'], "STARTING\n")
82
- running[adapter] = start_command(adapter)
83
- end
84
-
85
- def start_command(adapter)
86
- command = adapter.config['command']
87
- Array(command).map do |cmd|
88
- rout, wout = IO.pipe
89
- pid = spawn(adapter.config['path'], cmd, wout)
90
- Thread.new do
91
- log(adapter.config['name'], rout.gets) until rout.eof?
92
- end
93
- [pid, wout]
94
- end
95
- end
96
-
97
- def spawn(path, cmd, out)
98
- Process.spawn(
99
- "cd '#{path}' && " + command_template % cmd,
100
- pgroup: true,
101
- err: out,
102
- out: out
103
- )
104
- end
105
-
106
- def stop(adapter)
107
- return unless running[adapter]
108
- if should_stop?(adapter)
109
- really_stop(adapter)
110
- else
111
- stopping[adapter] ||= stop_delay
112
- stopping[adapter] -= interval
113
- end
114
- end
115
-
116
- def should_stop?(adapter)
117
- stopping[adapter] && stopping[adapter] <= 0
118
- end
119
-
120
- def really_stop(adapter)
121
- log(adapter.config['name'], "STOPPING\n")
122
- stopping.delete(adapter)
123
- return unless running[adapter]
124
- running[adapter].each do |(pid, wout)|
125
- Process.kill('-TERM', pid)
126
- wout.close
127
- end
128
- running.delete(adapter)
129
- end
130
-
131
- def term_all
132
- running.values.flatten.each do |(pid)|
133
- Process.kill('-TERM', pid)
134
- end
135
- end
136
-
137
- def log(name, message)
138
- color = colors[name]
139
- $stdout.write("\e[#{color}m#{name.rjust(label_width)}:\e[0m #{message}")
140
- end
141
-
142
- def label_width
143
- @label_width ||= apps.keys.map(&:size).max
144
- end
145
-
146
- def assign_colors
147
- color_values = COLORS.values
148
- apps.keys.each_with_object({}) do |app, hash|
149
- hash[app] = color_values.shift
150
- end
151
- end
152
-
153
- def build_adapters
154
- apps.flat_map do |app, config|
155
- config.map do |type, worker_config|
156
- adapter = adapter_from_type(type)
157
- next if adapter.nil?
158
- adapter.new(
159
- config: worker_config.merge('name' => app, 'path' => config['path']),
160
- redis: redis
161
- )
162
- end
163
- end.compact
164
- end
165
-
166
- def adapter_from_type(type)
167
- case type
168
- when 'sidekiq'
169
- Adapters::Sidekiq
170
- when 'resque'
171
- Adapters::Resque
172
- when 'path'
173
- nil
174
- else
175
- fail "Unknown type #{type}"
176
- end
177
- end
178
- end
1
+ require_relative './honcho/runner'
@@ -1,3 +1,22 @@
1
1
  require_relative './adapters/base'
2
2
  require_relative './adapters/resque'
3
3
  require_relative './adapters/sidekiq'
4
+
5
+ module Honcho
6
+ module Adapters
7
+ module_function
8
+
9
+ def from_type(type)
10
+ case type
11
+ when 'sidekiq'
12
+ Adapters::Sidekiq
13
+ when 'resque'
14
+ Adapters::Resque
15
+ when 'path' # special config key that gets ignored
16
+ nil
17
+ else
18
+ raise "Unknown type #{type}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,25 +1,115 @@
1
- class Honcho
1
+ module Honcho
2
2
  module Adapters
3
3
  class Base
4
- def initialize(config:, redis:)
5
- @redis = redis
4
+ def initialize(config:, redis:, runner:)
6
5
  @config = config
6
+ @redis = redis
7
+ @runner = runner
8
+ @running = false
9
+ @stopping = false
10
+ end
11
+
12
+ attr_reader :config, :redis, :runner, :running, :stopping
13
+
14
+ def check_for_work
15
+ if run?
16
+ start
17
+ else
18
+ stop
19
+ end
20
+ end
21
+
22
+ def name
23
+ config['name']
24
+ end
25
+
26
+ def commands
27
+ Array(config['command'])
7
28
  end
8
29
 
9
- attr_reader :config, :redis
30
+ def path
31
+ config['path']
32
+ end
10
33
 
11
34
  def run?
12
35
  work_to_do? || work_being_done?
13
36
  end
14
37
 
38
+ def running?
39
+ @running != false
40
+ end
41
+
42
+ def stopping?
43
+ @stopping != false
44
+ end
45
+
46
+ def start
47
+ @stopping = false
48
+ return if running?
49
+ log(name, "STARTING\n")
50
+ @running = start_command
51
+ end
52
+
53
+ def start_command
54
+ commands.map do |cmd|
55
+ rout, wout = IO.pipe
56
+ pid = spawn(path, cmd, wout)
57
+ Thread.new do
58
+ log(name, rout.gets) until rout.eof?
59
+ end
60
+ [pid, wout]
61
+ end
62
+ end
63
+
64
+ def stop
65
+ return unless running?
66
+ if should_stop?
67
+ really_stop
68
+ else
69
+ @stopping ||= stop_delay
70
+ @stopping -= interval
71
+ end
72
+ end
73
+
74
+ def should_stop?
75
+ stopping? && stopping <= 0
76
+ end
77
+
78
+ def really_stop
79
+ @stopping = false
80
+ return unless running?
81
+ log(name, "STOPPING\n")
82
+ running.each do |(pid, wout)|
83
+ Process.kill('-TERM', pid)
84
+ wout.close
85
+ end
86
+ @running = false
87
+ end
88
+
15
89
  private
16
90
 
91
+ def log(*args)
92
+ runner.log(*args)
93
+ end
94
+
95
+ def spawn(*args)
96
+ runner.spawn(*args)
97
+ end
98
+
99
+ def stop_delay
100
+ runner.stop_delay
101
+ end
102
+
103
+ def interval
104
+ runner.interval
105
+ end
106
+
17
107
  def work_to_do?
18
- fail NotImplementedError, "please define #{this.class.name}##{__method__}"
108
+ raise NotImplementedError, "please define #{this.class.name}##{__method__}"
19
109
  end
20
110
 
21
111
  def work_being_done?
22
- fail NotImplementedError, "please define #{this.class.name}##{__method__}"
112
+ raise NotImplementedError, "please define #{this.class.name}##{__method__}"
23
113
  end
24
114
  end
25
115
  end
@@ -1,4 +1,4 @@
1
- class Honcho
1
+ module Honcho
2
2
  module Adapters
3
3
  class Resque < Base
4
4
  private
@@ -1,4 +1,4 @@
1
- class Honcho
1
+ module Honcho
2
2
  module Adapters
3
3
  class Sidekiq < Base
4
4
  private
@@ -0,0 +1,121 @@
1
+ require 'curses'
2
+ require 'redis'
3
+ require 'time'
4
+ require 'stringio'
5
+ require 'yaml'
6
+ require_relative './adapters'
7
+
8
+ Thread.abort_on_exception = true
9
+
10
+ module Honcho
11
+ class Runner
12
+ COLORS = {
13
+ red: '0;31',
14
+ green: '0;32',
15
+ yellow: '0;33',
16
+ blue: '0;34',
17
+ magenta: '0;35',
18
+ cyan: '0;36',
19
+ bright_red: '1;31',
20
+ bright_green: '1;32',
21
+ bright_yellow: '1;33',
22
+ bright_blue: '1;34',
23
+ bright_magenta: '1;35',
24
+ bright_cyan: '1;36'
25
+ }.freeze
26
+
27
+ def initialize(options)
28
+ @config_file_path = options[:config]
29
+ @root_path = File.expand_path('..', @config_file_path)
30
+ @running = {}
31
+ @stopping = {}
32
+ @redis = Redis.new
33
+ @adapters = build_adapters
34
+ @colors = assign_colors
35
+ end
36
+
37
+ attr_reader :config_file_path, :root_path, :adapters, :running, :stopping, :redis, :colors
38
+
39
+ def run
40
+ trap(:INT) { term_all && exit }
41
+ trap(:TERM) { term_all && exit }
42
+ loop do
43
+ check_for_work
44
+ sleep(interval)
45
+ end
46
+ end
47
+
48
+ def log(name, message)
49
+ color = colors[name]
50
+ $stdout.write("\e[#{color}m#{name.rjust(label_width)}:\e[0m #{message}")
51
+ end
52
+
53
+ def spawn(path, cmd, out)
54
+ Process.spawn(
55
+ "cd '#{root_path}/#{path}' && " + command_template % cmd,
56
+ pgroup: true,
57
+ err: out,
58
+ out: out
59
+ )
60
+ end
61
+
62
+ def interval
63
+ config['interval'] || 2
64
+ end
65
+
66
+ def stop_delay
67
+ config['stop_delay'] || 30
68
+ end
69
+
70
+ private
71
+
72
+ def apps
73
+ config['apps']
74
+ end
75
+
76
+ def command_template
77
+ config['command_template'] || '%s'
78
+ end
79
+
80
+ def config
81
+ @config ||= YAML.load_file(config_file_path)
82
+ end
83
+
84
+ def check_for_work
85
+ adapters.each(&:check_for_work)
86
+ end
87
+
88
+ def term_all
89
+ adapters.each(&:really_stop)
90
+ end
91
+
92
+ def label_width
93
+ @label_width ||= apps.keys.map(&:size).max
94
+ end
95
+
96
+ def assign_colors
97
+ color_values = COLORS.values
98
+ apps.keys.each_with_object({}) do |app, hash|
99
+ hash[app] = color_values.shift
100
+ end
101
+ end
102
+
103
+ def build_adapters
104
+ apps.flat_map do |app, config|
105
+ config.map do |type, worker_config|
106
+ build_adapter(app, config, type, worker_config)
107
+ end
108
+ end.compact
109
+ end
110
+
111
+ def build_adapter(app, config, type, worker_config)
112
+ adapter = Adapters.from_type(type)
113
+ return if adapter.nil?
114
+ adapter.new(
115
+ config: worker_config.merge('name' => app, 'path' => config['path']),
116
+ redis: redis,
117
+ runner: self
118
+ )
119
+ end
120
+ end
121
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honcho
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Morgn
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-11 00:00:00.000000000 Z
11
+ date: 2016-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: curses
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: Sidekiq- and Resque-aware process manager (alternative to Foreman)
28
42
  email:
29
43
  - tim@timmorgan.org
@@ -38,6 +52,7 @@ files:
38
52
  - lib/honcho/adapters/base.rb
39
53
  - lib/honcho/adapters/resque.rb
40
54
  - lib/honcho/adapters/sidekiq.rb
55
+ - lib/honcho/runner.rb
41
56
  homepage: https://github.com/seven1m/honcho
42
57
  licenses:
43
58
  - MIT