honcho 1.0.2 → 1.1.0

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 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