sam-shepherd 0.8.2 → 0.9.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
  SHA256:
3
- metadata.gz: 0f74445b9a25d5ab1038deeaa2b3ce215c657b3df4888ee6e01e2586919c189b
4
- data.tar.gz: '04593b3b97f42e5d083aa5eea1d20624815083eee2eb2e079a6505b8d83d5889'
3
+ metadata.gz: 06abe7f1d5b6691e01a1df7433ae4ed8d9f2ec0e153c3fe82dfc558677c33fa7
4
+ data.tar.gz: 204385c6f7c60f1ce2c18b7a44d98af94fc52850f2770725ab0418eeb158fa86
5
5
  SHA512:
6
- metadata.gz: 5dd1b4de7a1883409ed26e98f7156a1d761eb806e7a290540c8729cfda8916a972481e6c048bd715bf270c6d4bdb68bccc17c9e47cf0034538f9fc349e95b153
7
- data.tar.gz: 286afdcf6ba6806752e879ab7798ad6c6e4a0bef0b87940bda672105a00cade3b1f7e24fafd099cac333027213661079cce2899933a13bf61950ac16417a02db
6
+ metadata.gz: 6b2b3ea89e0147e321dbe787e25ab4480dca6144c4de4fb1a5369d9272a950caffb4b36120191134fa2d0cae3b9cc796f4f81e128e3695ed12f5dbc7f665665e
7
+ data.tar.gz: edc6d81e4befc3090877d25157681c888095922aea8cc7dffd39fae8a1407a4cf7459c6fad83412a3fb3e07079ab43ccd03c220c747c10f8cf7a3d005c8fed45
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sam-shepherd (0.8.2)
4
+ sam-shepherd (0.9.0)
5
5
  hanami-cli (~> 0.2)
6
6
  tty-command (~> 0.7)
7
7
 
@@ -22,7 +22,7 @@ GEM
22
22
  descendants_tracker (~> 0.0.4)
23
23
  ice_nine (~> 0.11.0)
24
24
  thread_safe (~> 0.3, >= 0.3.1)
25
- backports (3.11.1)
25
+ backports (3.11.3)
26
26
  builder (3.2.3)
27
27
  childprocess (0.9.0)
28
28
  ffi (~> 1.0, >= 1.0.11)
@@ -46,7 +46,7 @@ GEM
46
46
  backports (>= 3.8.0)
47
47
  cucumber-tag_expressions (~> 1.1.0)
48
48
  gherkin (>= 5.0.0)
49
- cucumber-expressions (5.0.14)
49
+ cucumber-expressions (5.0.17)
50
50
  cucumber-tag_expressions (1.1.1)
51
51
  cucumber-wire (0.0.1)
52
52
  descendants_tracker (0.0.4)
@@ -57,7 +57,7 @@ GEM
57
57
  equatable (0.5.0)
58
58
  erubis (2.7.0)
59
59
  ffi (1.9.23)
60
- flay (2.10.0)
60
+ flay (2.11.0)
61
61
  erubis (~> 2.7.0)
62
62
  path_expander (~> 1.0)
63
63
  ruby_parser (~> 3.0)
@@ -114,7 +114,7 @@ GEM
114
114
  nenv (~> 0.1)
115
115
  shellany (~> 0.0)
116
116
  parallel (1.12.1)
117
- parser (2.5.0.5)
117
+ parser (2.5.1.0)
118
118
  ast (~> 2.4.0)
119
119
  pastel (0.7.2)
120
120
  equatable (~> 0.5.0)
@@ -125,7 +125,7 @@ GEM
125
125
  coderay (~> 1.1.0)
126
126
  method_source (~> 0.9.0)
127
127
  public_suffix (3.0.2)
128
- puma (3.11.3)
128
+ puma (3.11.4)
129
129
  rack (2.0.4)
130
130
  rack-protection (2.0.1)
131
131
  rack
@@ -135,10 +135,10 @@ GEM
135
135
  rb-fsevent (0.10.3)
136
136
  rb-inotify (0.9.10)
137
137
  ffi (>= 0.5.0, < 2)
138
- reek (4.8.0)
138
+ reek (4.8.1)
139
139
  codeclimate-engine-rb (~> 0.4.0)
140
140
  parser (>= 2.5.0.0, < 2.6)
141
- rainbow (~> 3.0)
141
+ rainbow (>= 2.0, < 4.0)
142
142
  rspec (3.7.0)
143
143
  rspec-core (~> 3.7.0)
144
144
  rspec-expectations (~> 3.7.0)
@@ -152,7 +152,7 @@ GEM
152
152
  diff-lcs (>= 1.2.0, < 2.0)
153
153
  rspec-support (~> 3.7.0)
154
154
  rspec-support (3.7.1)
155
- rubocop (0.54.0)
155
+ rubocop (0.55.0)
156
156
  parallel (~> 1.10)
157
157
  parser (>= 2.5)
158
158
  powerpack (~> 0.1)
@@ -173,7 +173,7 @@ GEM
173
173
  ruby_parser (~> 3.8)
174
174
  tty-which (~> 0.3.0)
175
175
  virtus (~> 1.0)
176
- sexp_processor (4.10.1)
176
+ sexp_processor (4.11.0)
177
177
  shellany (0.0.1)
178
178
  simplecov (0.16.1)
179
179
  docile (~> 1.1)
@@ -190,7 +190,7 @@ GEM
190
190
  tilt (2.0.8)
191
191
  transproc (1.0.2)
192
192
  tty-color (0.4.2)
193
- tty-command (0.7.0)
193
+ tty-command (0.8.0)
194
194
  pastel (~> 0.7.0)
195
195
  tty-which (0.3.0)
196
196
  unicode-display_width (1.3.0)
data/README.md CHANGED
@@ -1,8 +1,7 @@
1
1
  # Sam
2
2
 
3
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/a8c24394996582c1b59f/maintainability)](https://codeclimate.com/github/VAGAScom/sam/maintainability)
4
- [![Gem Version](https://badge.fury.io/rb/sam.svg)](https://badge.fury.io/rb/sam)
5
- [![Build Status](https://travis-ci.org/VAGAScom/sam.svg?branch=master)](https://travis-ci.org/VAGAScom/sam)
4
+ [![Gem Version](https://badge.fury.io/rb/sam-shepherd.svg)](https://badge.fury.io/rb/sam)
6
5
  [![Dependency Status](https://beta.gemnasium.com/badges/github.com/VAGAScom/sam.svg)](https://beta.gemnasium.com/projects/github.com/VAGAScom/sam)
7
6
 
8
7
 
@@ -29,13 +28,8 @@ Or install it yourself as:
29
28
 
30
29
  ## Usage
31
30
 
32
- **Sam** comes with the following commands:
31
+ **Sam** is pretty straightforward and you can rely on its internal documentation. By invoking `sam` you can see the list of available commands. For a more in-depth help for each command, you can pass a `-h` flag to it, like this: `sam run -h`
33
32
 
34
- 1. `unicorn start`: starts an `unicorn` instance
35
- 2. `unicorn stop`: stops an `unicorn` instance
36
- 3. `unicorn monitor`: start a monitoring loop over the running `unicorn` instance. It will forward the signals `HUP, USR2, TERM, QUIT, TTIN, TTOU` to the `unicorn` instance. `HUP` will do an graceful reload of the application
37
- 4. `unicorn reload`: reloads the `unicorn` instance by issuing a `USR2` followed by a `QUIT` signal a few seconds later
38
- 5. `unicorn run`: starts and monitors an `unicorn` instance
39
33
 
40
34
  ## Development
41
35
 
data/lib/sam/cli.rb CHANGED
@@ -1,42 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'hanami/cli'
4
+ require_relative 'servers'
4
5
 
5
6
  module Sam
6
7
  module CLI
7
8
  module Commands
8
9
  extend Hanami::CLI::Registry
9
10
 
10
- require_relative 'cli/version'
11
+ require_relative 'commands/version'
12
+ require_relative 'commands/spawner'
13
+ require_relative 'commands/reaper'
14
+ require_relative 'commands/reloader'
15
+ require_relative 'commands/monitor'
16
+ require_relative 'commands/runner'
11
17
 
12
18
  register 'version', Version
13
- begin
14
- gem 'unicorn'
15
- require_relative 'unicorn'
16
- require_relative 'cli/unicorn'
17
- register 'unicorn' do |cmd|
18
- cmd.register 'start', Unicorn::Spawner
19
- cmd.register 'stop', Unicorn::Reaper
20
- cmd.register 'reload', Unicorn::Reloader
21
- cmd.register 'monitor', Unicorn::Monitor
22
- cmd.register 'run', Unicorn::Runner
23
- end
24
- rescue Gem::LoadError
25
- warn 'Unicorn not found. Not loading unicorn commands'
26
- end
27
-
28
- begin
29
- gem 'puma'
30
- require_relative 'puma'
31
- require_relative 'cli/puma'
32
- register 'puma' do |cmd|
33
- cmd.register 'start', Puma::Spawner
34
- cmd.register 'stop', Puma::Reaper
35
- # cmd.register 'stop', Puma::Reaper
36
- end
37
- rescue Gem::LoadError
38
- warn 'Puma not found. Not loading puma commands'
39
- end
19
+ register 'start', Spawner
20
+ register 'stop', Reaper
21
+ register 'reload', Reloader
22
+ register 'monitor', Monitor
23
+ register 'run', Runner
40
24
  end
41
25
  end
42
26
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sam
4
+ module CLI
5
+ module Commands
6
+ class Monitor < Hanami::CLI::Command
7
+ desc 'Monitor an already running server'
8
+
9
+ # rubocop:disable Metrics/LineLength
10
+ argument :server, required: true, values: %w[unicorn puma], desc: 'The application server to be started'
11
+ argument :config, required: true, desc: 'The path to the server configuration file'
12
+
13
+ option :timeout, aliases: ['-t'], default: 10, desc: 'The amount of time waiting for the server process to fork new workers'
14
+
15
+ example [
16
+ 'unicorn config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
17
+ ]
18
+ # rubocop:enable Metrics/LineLength
19
+
20
+ def call(server:, config:, timeout:)
21
+ path = Pathname.new(Dir.pwd).join(config)
22
+ Sam::Servers::Shepherd.new.call(server: server, config: path, timeout: timeout)
23
+ rescue Errors::ProcessNotFound
24
+ warn "#{server} exited"
25
+ exit 1
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sam
4
+ module CLI
5
+ module Commands
6
+ class Reaper < Hanami::CLI::Command
7
+ # rubocop:disable Metrics/LineLength
8
+ desc 'Stops the server process'
9
+ argument :server, required: true, values: %w[unicorn puma], desc: 'The application server to be started'
10
+ argument :config, required: true, desc: 'The path to the server configuration file'
11
+
12
+ example [
13
+ 'puma config/puma/development.rb # Stops the server in production mode using the config/puma/development.rb config file'
14
+ ]
15
+ # rubocop:enable Metrics/LineLength
16
+
17
+ def call(server:, config:)
18
+ exit 1 unless server_found(server)
19
+ path = Pathname.new(Dir.pwd).join(config)
20
+ result = Sam::Servers::Predator.new.call(server: server, config: path)
21
+ warn "Hunted one #{server} with pid #{result}"
22
+ rescue Errors::PidfileNotFound
23
+ warn "No running #{server}s found"
24
+ exit 1
25
+ rescue Errors::ConfigfileNotFound => ex
26
+ warn ex.message
27
+ exit 1
28
+ end
29
+
30
+ private
31
+
32
+ def server_found(server)
33
+ gem server
34
+ rescue Gem::LoadError
35
+ warn "#{server} not found. Make sure it's in your Gemfile"
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sam
4
+ module CLI
5
+ module Commands
6
+ class Reloader < Hanami::CLI::Command
7
+ desc 'Reloads the code on a running server'
8
+
9
+ # rubocop:disable Metrics/LineLength
10
+ argument :server, required: true, values: %w[unicorn puma], desc: 'The application server to be started'
11
+ argument :config, required: true, desc: 'The path to the server configuration file'
12
+
13
+ option :timeout, aliases: ['-t'], default: 10, desc: 'The amount of time waiting for the server process to fork new workers'
14
+
15
+ example [
16
+ 'puma config/server_settings.rb # Starts the server in test mode using the config/server_settings.rb config file',
17
+ 'unicorn config/server_settings.rb # Starts the server in production mode using the config/server_settings.rb config file'
18
+ ]
19
+ # rubocop:enable Metrics/LineLength
20
+
21
+ def call(server:, config:, timeout:)
22
+ path = Pathname.new(Dir.pwd).join(config)
23
+ Sam::Servers::Cloner.new.call(server: server, config: path, timeout: timeout)
24
+ rescue Errors::ProcessNotFound
25
+ warn "No running #{server}s found"
26
+ exit 1
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sam
4
+ module CLI
5
+ module Commands
6
+ class Runner < Hanami::CLI::Command
7
+ desc 'Starts and monitor an already running server'
8
+ # rubocop:disable Metrics/LineLength
9
+ argument :server, required: true, values: %w[unicorn puma], desc: 'The application server to be started'
10
+ argument :config, required: true, desc: 'The path to the server configuration file'
11
+
12
+ option :env, values: %w[production development test staging], default: 'production', desc: 'RACK_ENV to be used', aliases: ['-e']
13
+ option :timeout, aliases: ['-t'], default: 10, desc: 'The amount of time waiting for the server process to fork new workers'
14
+
15
+ example [
16
+ 'puma config/server_settings.rb -e test # Starts and monitors the server in test mode using the config/server_settings.rb config file',
17
+ 'unicorn config/server_settings.rb #Starts and monitors the server in production mode using the config/server_settings.rb config file'
18
+ ]
19
+ # rubocop:enable Metrics/LineLength
20
+
21
+ def call(server:, config:, env:, timeout:)
22
+ Sam::Servers::Breeder.new.call(server: server, config: config, env: env)
23
+ Sam::Servers::Monitor.new.call(server: server, config: config, timeout: timeout)
24
+ rescue Sam::Errors::ProcessNotFound
25
+ warn "#{server} exited"
26
+ exit 1
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hanami/cli'
4
+
5
+ module Sam
6
+ module CLI
7
+ module Commands
8
+ class Spawner < Hanami::CLI::Command
9
+ desc 'Starts the server'
10
+
11
+ # rubocop:disable Metrics/LineLength
12
+ argument :server, required: true, values: %w[unicorn puma], desc: 'The application server to be started'
13
+ argument :config, required: true, desc: 'The path to the server configuration file'
14
+
15
+ option :env, values: %w[production development test staging], default: 'production', desc: 'RACK_ENV to be used', aliases: ['-e']
16
+
17
+ example [
18
+ 'puma config/server_settings.rb -e test # Starts the server in test mode using the config/server_settings.rb config file',
19
+ 'unicorn config/server_settings.rb # Starts the server in production mode using the config/server_settings.rb config file'
20
+ ]
21
+ # rubocop:enable Metrics/LineLength
22
+
23
+ def call(server:, config:, env:)
24
+ path_to_config = Pathname.new(Dir.pwd).join(config)
25
+ Sam::Servers::Breeder.new.call(server: server, config: path_to_config, env: env)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
File without changes
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ gem 'unicorn'
5
+ require 'unicorn'
6
+ require_relative 'servers/unicorn/identifier'
7
+ rescue Gem::LoadError
8
+ warn 'unicorn unavailable'
9
+ end
10
+
11
+ begin
12
+ gem 'puma'
13
+ require 'puma/cli'
14
+ require_relative 'servers/puma/identifier'
15
+ rescue Gem::LoadError
16
+ warn 'puma unavailable'
17
+ end
18
+
19
+ module Sam
20
+ module Servers
21
+ PUMA_PID = ->(config) { Sam::Puma::Identifier.new.call(config) }
22
+ UNICORN_PID = ->(config) { Sam::Unicorn::Identifier.new.call(config) }
23
+ end
24
+ end
25
+
26
+ require_relative 'servers/breeder'
27
+ require_relative 'servers/cloner'
28
+ require_relative 'servers/predator'
29
+ require_relative 'servers/shepherd'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tty-command'
4
+
5
+ module Sam
6
+ module Servers
7
+ class Breeder
8
+ UNICORN = 'bundle exec unicorn -D -c ${CONF_PATH} -E ${RACK_ENV}'
9
+ PUMA = 'bundle exec puma -C ${CONF_PATH} -e ${RACK_ENV}'
10
+
11
+ def initialize
12
+ @cmd = TTY::Command.new(timeout: 120, printer: :quiet)
13
+ end
14
+
15
+ def call(server:, config:, env: 'production')
16
+ cmdline = self.class.const_get(server.upcase)
17
+ @cmd.run(cmdline, env: { CONF_PATH: config, RACK_ENV: env })
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sam
4
+ module Servers
5
+ class Cloner
6
+ PUMA_PID = ->(config) { Sam::Puma::Identifier.new.call(config) }
7
+ UNICORN_PID = ->(config) { Sam::Unicorn::Identifier.new.call(config) }
8
+
9
+ UNICORN_STRATEGY = lambda do |config, timeout = 10|
10
+ pid = UNICORN_PID.call(config)
11
+ Process.kill('USR2', pid)
12
+ sleep timeout
13
+ newpid = UNICORN_PID.call(config)
14
+ Process.kill('QUIT', pid)
15
+ newpid
16
+ end
17
+
18
+ PUMA_STRATEGY = lambda do |config, _timeout|
19
+ settings = (::Puma::Configuration.new({}) { |conf| conf.load config.to_s }).load
20
+ Process.kill(settings.file_options[:preload_app] ? 'USR1' : 'USR2', PUMA_PID.call(config))
21
+ end
22
+
23
+ def call(server:, config:, timeout: 10)
24
+ restart_strategy(server, config, timeout)
25
+ end
26
+
27
+ private
28
+
29
+ def restart_strategy(server, config, timeout)
30
+ self.class.const_get("#{server.upcase}_STRATEGY").call(config, timeout)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sam
4
+ module Servers
5
+ class Predator
6
+ PUMA_PID = ->(config) { Sam::Puma::Identifier.new.call(config) }
7
+ UNICORN_PID = ->(config) { Sam::Unicorn::Identifier.new.call(config) }
8
+
9
+ def call(server:, config:)
10
+ @server = server
11
+ @config = config
12
+ Process.kill(signal, pid) && pid
13
+ end
14
+
15
+ private
16
+
17
+ def signal
18
+ @server == 'unicorn' ? 'QUIT' : 'INT'
19
+ end
20
+
21
+ def pid
22
+ @pid ||= self.class.const_get("#{@server.upcase}_PID").call(@config)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/cli'
4
-
5
3
  module Sam
6
4
  module Puma
7
5
  class Identifier
@@ -1,30 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sam
4
- module Puma
4
+ module Servers
5
5
  class Shepherd
6
6
  def initialize
7
- setup_signal_handlers
7
+ trap_signals
8
8
  @restarting = false
9
9
  end
10
10
 
11
- def call(path, timeout: 10)
11
+ def call(server:, config:, timeout: 0.5)
12
+ @config = config
13
+ @server = server
12
14
  @timeout = timeout
13
- @config ||= Pathname.new(Dir.pwd).join(path)
14
15
  loop do
15
- alive?
16
- sleep 1
16
+ Process.kill(0, pid)
17
+ rescue Errno::ESRCH
18
+ raise Errors::ProcessNotFound unless @restarting
17
19
  end
18
20
  end
19
21
 
20
22
  private
21
23
 
22
24
  def pid
23
- @pid ||= Identifier.new.call(@config)
25
+ @pid ||= self.class.const_get("Sam::Servers::#{@server.upcase}_PID").call(@config)
24
26
  end
25
27
 
26
- def setup_signal_handlers
27
- trap('HUP') { reload_puma }
28
+ def trap_signals
29
+ trap('HUP') { reload_server }
28
30
  trap('QUIT') { forward_signal('QUIT') }
29
31
  trap('USR2') { forward_signal('USR2') }
30
32
  trap('TTOU') { forward_signal('TTOU') }
@@ -33,23 +35,17 @@ module Sam
33
35
  trap('INT') { forward_signal('INT') }
34
36
  end
35
37
 
36
- def reload_puma
38
+ def reload_server
37
39
  @restarting = true
38
- @pid = Cloner.new.call(@config, timeout: @timeout)
40
+ @pid = Cloner.new.call(server: @server, config: @config, timeout: @timeout)
39
41
  @restarting = false
40
42
  end
41
43
 
42
44
  def forward_signal(signal)
43
- warn "Sending #{signal} to unicorn #{pid}"
44
45
  Process.kill(signal, pid)
45
46
  end
46
47
 
47
- def alive?
48
- return if @restarting
49
- Process.kill(0, pid)
50
- rescue Errno::ESRCH
51
- raise Errors::ProcessNotFound
52
- end
48
+ def heartbeat; end
53
49
  end
54
50
  end
55
51
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'unicorn'
4
-
5
3
  module Sam
6
4
  module Unicorn
7
5
  class Identifier
data/lib/sam/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sam
4
- VERSION = '0.8.2'
4
+ VERSION = '0.9.0'
5
5
  end
data/sam.gemspec CHANGED
@@ -18,12 +18,12 @@ Gem::Specification.new do |spec|
18
18
 
19
19
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
20
20
  # to allow pushing to a single host or delete this section to allow pushing to any host.
21
- #if spec.respond_to?(:metadata)
21
+ # if spec.respond_to?(:metadata)
22
22
  # spec.metadata['allowed_push_host'] = 'https://gemfury.org'
23
- #else
23
+ # else
24
24
  # raise 'RubyGems 2.0 or newer is required to protect against ' \
25
25
  # 'public gem pushes.'
26
- #end
26
+ # end
27
27
 
28
28
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
29
29
  f.match(%r{^(test|spec|features)/})
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sam-shepherd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - mereghost
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-16 00:00:00.000000000 Z
11
+ date: 2018-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hanami-cli
@@ -258,29 +258,20 @@ files:
258
258
  - exe/sam
259
259
  - lib/sam.rb
260
260
  - lib/sam/cli.rb
261
- - lib/sam/cli/puma.rb
262
- - lib/sam/cli/puma/monitor.rb
263
- - lib/sam/cli/puma/reaper.rb
264
- - lib/sam/cli/puma/spawner.rb
265
- - lib/sam/cli/unicorn.rb
266
- - lib/sam/cli/unicorn/monitor.rb
267
- - lib/sam/cli/unicorn/reaper.rb
268
- - lib/sam/cli/unicorn/reloader.rb
269
- - lib/sam/cli/unicorn/runner.rb
270
- - lib/sam/cli/unicorn/spawner.rb
271
- - lib/sam/cli/version.rb
261
+ - lib/sam/commands/monitor.rb
262
+ - lib/sam/commands/reaper.rb
263
+ - lib/sam/commands/reloader.rb
264
+ - lib/sam/commands/runner.rb
265
+ - lib/sam/commands/spawner.rb
266
+ - lib/sam/commands/version.rb
272
267
  - lib/sam/errors.rb
273
- - lib/sam/puma.rb
274
- - lib/sam/puma/breeder.rb
275
- - lib/sam/puma/identifier.rb
276
- - lib/sam/puma/predator.rb
277
- - lib/sam/puma/shepherd.rb
278
- - lib/sam/unicorn.rb
279
- - lib/sam/unicorn/breeder.rb
280
- - lib/sam/unicorn/cloner.rb
281
- - lib/sam/unicorn/identifier.rb
282
- - lib/sam/unicorn/predator.rb
283
- - lib/sam/unicorn/shepherd.rb
268
+ - lib/sam/servers.rb
269
+ - lib/sam/servers/breeder.rb
270
+ - lib/sam/servers/cloner.rb
271
+ - lib/sam/servers/predator.rb
272
+ - lib/sam/servers/puma/identifier.rb
273
+ - lib/sam/servers/shepherd.rb
274
+ - lib/sam/servers/unicorn/identifier.rb
284
275
  - lib/sam/version.rb
285
276
  - sam.gemspec
286
277
  homepage: https://github.com/vagas/sam
data/lib/sam/cli/puma.rb DELETED
@@ -1,4 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'puma/spawner'
4
- require_relative 'puma/reaper'
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Puma
7
- class Monitor < Hanami::CLI::Command
8
- # rubocop:disable Metrics/LineLength
9
- desc 'Monitor an already running unicorn'
10
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
11
- option :timeout, type: :integer, desc: 'The number of seconds to wait for starting the unicorn server', aliases: ['-t']
12
- example [
13
- '--config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
14
- ]
15
- # rubocop:enable Metrics/LineLength
16
-
17
- def call(config:)
18
- path = Pathname.new(Dir.pwd).join(config)
19
- Sam::Puma::Shepherd.new.call(path)
20
- rescue Errors::ProcessNotFound
21
- warn 'Puma exited'
22
- exit 1
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Puma
7
- class Reaper < Hanami::CLI::Command
8
- NO_MORE_PUMAS = 'Pumas are extinct =('
9
-
10
- # rubocop:disable Metrics/LineLength
11
- desc 'Stops the puma process'
12
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
13
-
14
- example [
15
- '-config config/server_settings.rb Stops the server in production mode using the config/server_settings.rb config file'
16
- ]
17
- # rubocop:enable Metrics/LineLength
18
-
19
- def call(config:)
20
- path = Pathname.new(Dir.pwd).join(config)
21
- result = Sam::Puma::Predator.new.call(path)
22
- warn "Hunted a puma with pid #{result}"
23
- rescue Errors::PidfileNotFound
24
- warn NO_MORE_PUMAS
25
- exit 1
26
- rescue Errors::ConfigfileNotFound => ex
27
- warn ex.message
28
- exit 1
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Puma
7
- class Spawner < Hanami::CLI::Command
8
- # rubocop:disable Metrics/LineLength
9
- desc 'Start the puma process'
10
-
11
- option :environment, values: %w[production development test staging], default: 'production', desc: 'RACK_ENV to be used', aliases: %w[--env -e]
12
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/puma/production.rb', aliases: ['-c']
13
-
14
- example [
15
- '-e development #Starts the server in development mode',
16
- '-e production --config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
17
- ]
18
- # rubocop:enable Metrics/LineLength
19
-
20
- def call(environment:, config:)
21
- config = Pathname.new(Dir.pwd).join(config)
22
- Sam::Puma::Breeder.new.call(environment, config)
23
- warn "We have a newborn puma for environment #{environment} and configuration file: #{config}"
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'unicorn/monitor'
4
- require_relative 'unicorn/reaper'
5
- require_relative 'unicorn/reloader'
6
- require_relative 'unicorn/spawner'
7
- require_relative 'unicorn/runner'
8
-
9
- # Upstart reload sends SIGHUP to the process
10
- # Systemd expects an ExecReload= that syncronously reloads the confs.
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Unicorn
7
- class Monitor < Hanami::CLI::Command
8
- # rubocop:disable Metrics/LineLength
9
- desc 'Monitor an already running unicorn'
10
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
11
- option :timeout, type: :integer, desc: 'The number of seconds to wait for starting the unicorn server', aliases: ['-t']
12
- example [
13
- '--config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
14
- ]
15
- # rubocop:enable Metrics/LineLength
16
-
17
- def call(config:)
18
- path = Pathname.new(Dir.pwd).join(config)
19
- Sam::Unicorn::Shepherd.new.call(path)
20
- rescue Errors::ProcessNotFound
21
- warn 'Unicorn exited'
22
- exit 1
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Unicorn
7
- class Reaper < Hanami::CLI::Command
8
- NO_MORE_UNICORNS = 'Unicorns are already extinct'
9
-
10
- # rubocop:disable Metrics/LineLength
11
- desc 'Stops the unicorn process'
12
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
13
-
14
- example [
15
- '-config config/server_settings.rb Stops the server in production mode using the config/server_settings.rb config file'
16
- ]
17
- # rubocop:enable Metrics/LineLength
18
-
19
- def call(config:)
20
- path = Pathname.new(Dir.pwd).join(config)
21
- result = Sam::Unicorn::Predator.new.call(path)
22
- warn "Hunted unicorn with pid #{result}"
23
- rescue Errors::PidfileNotFound
24
- warn NO_MORE_UNICORNS
25
- exit 1
26
- rescue Errors::ConfigfileNotFound => ex
27
- warn ex.message
28
- exit 1
29
- end
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Unicorn
7
- class Reloader < Hanami::CLI::Command
8
- # rubocop:disable Metrics/LineLength
9
- desc 'Reloads the unicorn configuration'
10
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
11
-
12
- example [
13
- '--config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
14
- ]
15
- # rubocop:enable Metrics/LineLength
16
-
17
- def call(config)
18
- path = Pathname.new(Dir.pwd).join(config)
19
- Sam::Unicorn::Cloner.new.call(path)
20
- end
21
- end
22
- end
23
- end
24
- end
25
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Unicorn
7
- class Runner < Hanami::CLI::Command
8
- # rubocop:disable Metrics/LineLength
9
- desc 'Monitor an already running unicorn'
10
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
11
- option :environment, values: %w[production development test staging], default: 'production', desc: 'RACK_ENV to be used', aliases: %w[--env -e]
12
- option :timeout, type: :integer, desc: 'The number of seconds to wait for starting the unicorn server', default: 5, aliases: ['-t']
13
- example [
14
- '--config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file',
15
- '-t 20 # Waits for 20 seconds for the master process to fork',
16
- '-e production --config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
17
-
18
- ]
19
- # rubocop:enable Metrics/LineLength
20
-
21
- def call(config:, environment:, timeout:)
22
- path = Pathname.new(Dir.pwd).join(config)
23
- Sam::Unicorn::Breeder.new.call(environment, path)
24
- Sam::Unicorn::Shepherd.new.call(path, timeout: Integer(timeout))
25
- rescue Errors::ProcessNotFound
26
- warn 'Unicorn seems dead.'
27
- exit 1
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module CLI
5
- module Commands
6
- module Unicorn
7
- class Spawner < Hanami::CLI::Command
8
- # rubocop:disable Metrics/LineLength
9
- desc 'Start the unicorn process'
10
-
11
- option :environment, values: %w[production development test staging], default: 'production', desc: 'RACK_ENV to be used', aliases: %w[--env -e]
12
- option :config, type: :path, desc: 'The path to the server configuration', default: 'config/unicorn/production.rb', aliases: ['-c']
13
-
14
- example [
15
- '-e development #Starts the server in development mode',
16
- '-e production --config=config/server_settings.rb #Starts the server in production mode using the config/server_settings.rb config file'
17
- ]
18
- # rubocop:enable Metrics/LineLength
19
-
20
- def call(environment:, config:)
21
- config = Pathname.new(Dir.pwd).join(config)
22
- Sam::Unicorn::Breeder.new.call(environment, config)
23
- warn "We have a newborn unicorn for environment #{environment} and configuration file: #{config}"
24
- end
25
- end
26
- end
27
- end
28
- end
29
- end
data/lib/sam/puma.rb DELETED
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'puma/identifier'
4
- require_relative 'puma/breeder'
5
- require_relative 'puma/predator'
6
- require_relative 'puma/shepherd'
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tty-command'
4
-
5
- module Sam
6
- module Puma
7
- class Breeder
8
- COMMAND = 'bundle exec puma -C ${CONF_PATH} -e ${RACK_ENV}'
9
-
10
- def call(env, config)
11
- cmd = TTY::Command.new(timeout: 120)
12
- cmd.run(COMMAND, env: { CONF_PATH: config, RACK_ENV: env })
13
- end
14
- end
15
- end
16
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module Puma
5
- class Predator
6
- def call(config)
7
- pid = Sam::Puma::Identifier.new.call(config)
8
- Process.kill('INT', pid) && pid
9
- end
10
- end
11
- end
12
- end
data/lib/sam/unicorn.rb DELETED
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'unicorn/identifier'
4
- require_relative 'unicorn/breeder'
5
- require_relative 'unicorn/predator'
6
- require_relative 'unicorn/cloner'
7
- require_relative 'unicorn/shepherd'
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'tty-command'
4
-
5
- module Sam
6
- module Unicorn
7
- class Breeder
8
- COMMAND = 'bundle exec unicorn -D -c ${CONF_PATH} -E ${RACK_ENV}'
9
-
10
- def call(env, config)
11
- cmd = TTY::Command.new(timeout: 120)
12
- cmd.run(COMMAND, env: { CONF_PATH: config, RACK_ENV: env })
13
- end
14
- end
15
- end
16
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module Unicorn
5
- class Cloner
6
- def call(config, timeout: 10)
7
- pid = Identifier.new.call(config)
8
- # warn "Cloning unicorn tagged #{pid}"
9
- Process.kill('USR2', pid)
10
- # warn "Waiting #{timeout} seconds for clone process to conclude..."
11
- sleep timeout
12
- newpid = Identifier.new.call(config)
13
- # warn "New unicorn cloned with tag #{newpid}. Reaping it's predecessor..."
14
- Process.kill('QUIT', pid)
15
- newpid
16
- # warn 'No one suspects a thing.'
17
- rescue Errno::ESRCH
18
- raise Errors::ProcessNotFound, "No process with PID #{pid} found"
19
- end
20
- end
21
- end
22
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module Unicorn
5
- class Predator
6
- def call(config)
7
- pid = Sam::Unicorn::Identifier.new.call(config)
8
- Process.kill('QUIT', pid) && pid
9
- end
10
- end
11
- end
12
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Sam
4
- module Unicorn
5
- class Shepherd
6
- def initialize
7
- setup_signal_handlers
8
- @restarting = false
9
- end
10
-
11
- def call(path, timeout: 10)
12
- @timeout = timeout
13
- @config ||= Pathname.new(Dir.pwd).join(path)
14
- loop do
15
- alive?
16
- sleep 1
17
- end
18
- end
19
-
20
- private
21
-
22
- def pid
23
- @pid ||= Identifier.new.call(@config)
24
- end
25
-
26
- def setup_signal_handlers
27
- trap('HUP') { reload_unicorn }
28
- trap('QUIT') { forward_signal('QUIT') }
29
- trap('USR2') { forward_signal('USR2') }
30
- trap('TTOU') { forward_signal('TTOU') }
31
- trap('TTIN') { forward_signal('TTIN') }
32
- trap('TERM') { forward_signal('QUIT') }
33
- trap('INT') { forward_signal('INT') }
34
- end
35
-
36
- def reload_unicorn
37
- @restarting = true
38
- @pid = Cloner.new.call(@config, timeout: @timeout)
39
- @restarting = false
40
- end
41
-
42
- def forward_signal(signal)
43
- warn "Sending #{signal} to unicorn #{pid}"
44
- Process.kill(signal, pid)
45
- end
46
-
47
- def alive?
48
- return if @restarting
49
- Process.kill(0, pid)
50
- rescue Errno::ESRCH
51
- raise Errors::ProcessNotFound
52
- end
53
- end
54
- end
55
- end