capistrano-sidekiq 2.0.0 → 2.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
  SHA256:
3
- metadata.gz: 1c3bd1a14a2effd1ef907aaab6d7cb391d25d5125ec22d7ff338f0e98e475e28
4
- data.tar.gz: f8a97dcfe1ecdbfecf0ad5ffbb90df8cf5653a4b67f3e6e745cfc91a295c546e
3
+ metadata.gz: 9b617c393c8abe0fd6379444f85885ae53eab75f4dacb33a186b6b4d7d097d97
4
+ data.tar.gz: af4b161e8f6131d85f6c12e1795f72bdec58d967dde832b9f1953efaf5245107
5
5
  SHA512:
6
- metadata.gz: 776cc57678b6b04bb80f94470c90cf7690b44a0cea4ead9abb543c99726104ad2218c87308ba273f7304defd0fe6e61a14bbb1c7b1edb003a8012abb58f7f135
7
- data.tar.gz: 0fb423476f90dbece2f3107196392e168b9bfd17e47eef25a94a47fb1f34dab4bb839317aa2f30c1b8acc2d7c23a76e35f7477567c1417d52f111900fd9759ca
6
+ metadata.gz: 6d1c9c37c0b8c61bbff72a2c1dc8fe236f7268e285771a0da2e8ab0ad20662b38643dd401448c203165e4311a60d093b19b49af484e57fba02516a83adaad49b
7
+ data.tar.gz: 0c63285e974b438b066057e6b7090b8411b55212b11439ff811ab2dfb8c5ebb5f97a10098989fa11ac87e8dae37251c3a23ed324006327acef0ea34fc59cdf53
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 2.6.5
1
+ ruby 3.1.2
data/README.md CHANGED
@@ -20,15 +20,15 @@ And then execute:
20
20
  require 'capistrano/sidekiq'
21
21
  install_plugin Capistrano::Sidekiq # Default sidekiq tasks
22
22
  # Then select your service manager
23
- install_plugin Capistrano::Sidekiq::Systemd
24
- # or
23
+ install_plugin Capistrano::Sidekiq::Systemd
24
+ # or
25
25
  install_plugin Capistrano::Sidekiq::Upstart # tests needed
26
- # or
26
+ # or
27
27
  install_plugin Capistrano::Sidekiq::Monit # tests needed
28
28
  ```
29
29
 
30
30
 
31
- Configurable options, shown here with defaults:
31
+ Configurable options - Please ensure you check your version's branch for the available settings - shown here with defaults:
32
32
 
33
33
  ```ruby
34
34
  :sidekiq_roles => :app
@@ -36,8 +36,19 @@ Configurable options, shown here with defaults:
36
36
  :sidekiq_pid => File.join(shared_path, 'tmp', 'pids', 'sidekiq.pid') # ensure this path exists in production before deploying.
37
37
  :sidekiq_env => fetch(:rack_env, fetch(:rails_env, fetch(:stage)))
38
38
  :sidekiq_log => File.join(shared_path, 'log', 'sidekiq.log')
39
+ # single config
40
+ :sidekiq_config => 'config/sidekiq.yml'
41
+ # per process config - process 1, process 2,... etc.
42
+ :sidekiq_config => [
43
+ 'config/sidekiq_config1.yml',
44
+ 'config/sidekiq_config2.yml'
45
+ ]
46
+ :sidekiq_concurrency => 25
47
+ :sidekiq_queue => %w(default high low)
48
+ :sidekiq_processes => 1 # number of systemd processes you want to start
39
49
 
40
50
  # sidekiq systemd options
51
+ :sidekiq_service_templates_path => 'config/deploy/templates' # to be used if a custom template is needed (filaname should be #{fetch(:sidekiq_service_unit_name)}.service.capistrano.erb or sidekiq.service.capistrano.erb
41
52
  :sidekiq_service_unit_name => 'sidekiq'
42
53
  :sidekiq_service_unit_user => :user # :system
43
54
  :sidekiq_enable_lingering => true
@@ -50,10 +61,11 @@ Configurable options, shown here with defaults:
50
61
  :monit_bin => '/usr/bin/monit'
51
62
  :sidekiq_monit_default_hooks => true
52
63
  :sidekiq_monit_group => nil
53
- :sidekiq_service_name => "sidekiq_#{fetch(:application)}"
64
+ :sidekiq_service_name => "sidekiq_#{fetch(:application)}"
54
65
 
55
66
  :sidekiq_user => nil #user to run sidekiq as
56
67
  ```
68
+ See `capistrano/sidekiq/helpers.rb` for other undocumented configuration settings.
57
69
 
58
70
  ## Known issues with Capistrano 3
59
71
 
@@ -86,6 +98,14 @@ If your deploy user has no need in `sudo` for using monit, you can disable it as
86
98
  set :sidekiq_monit_use_sudo, false
87
99
  ```
88
100
 
101
+ ## Configuring the log files on systems with less recent Systemd versions
102
+
103
+ The template used by this project assumes a recent version of Systemd (v240+, e.g. Ubuntu 20.04).
104
+
105
+ On systems with a less recent version, the `append:` functionality is not supported, and the Sidekiq log messages are sent to the syslog.
106
+
107
+ It's possible to workaround this limitation by configuring the system logger to filter the Sidekiq messages; see [wiki](/../../wiki/Configuring-append-mode-log-files-via-Syslog-NG).
108
+
89
109
  ## Contributing
90
110
 
91
111
  1. Fork it
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'capistrano/sidekiq/version'
@@ -0,0 +1,60 @@
1
+ module Capistrano
2
+ module Sidekiq::Helpers
3
+
4
+ def sidekiq_require
5
+ if fetch(:sidekiq_require)
6
+ "--require #{fetch(:sidekiq_require)}"
7
+ end
8
+ end
9
+
10
+ def sidekiq_config
11
+ if fetch(:sidekiq_config)
12
+ "--config #{fetch(:sidekiq_config)}"
13
+ end
14
+ end
15
+
16
+ def sidekiq_concurrency
17
+ if fetch(:sidekiq_concurrency)
18
+ "--concurrency #{fetch(:sidekiq_concurrency)}"
19
+ end
20
+ end
21
+
22
+ def sidekiq_queues
23
+ Array(fetch(:sidekiq_queue)).map do |queue|
24
+ "--queue #{queue}"
25
+ end.join(' ')
26
+ end
27
+
28
+ def sidekiq_logfile
29
+ fetch(:sidekiq_log)
30
+ end
31
+
32
+ def switch_user(role)
33
+ su_user = sidekiq_user(role)
34
+ if su_user == role.user
35
+ yield
36
+ else
37
+ as su_user do
38
+ yield
39
+ end
40
+ end
41
+ end
42
+
43
+ def sidekiq_user(role = nil)
44
+ if role.nil?
45
+ fetch(:sidekiq_user)
46
+ else
47
+ properties = role.properties
48
+ properties.fetch(:sidekiq_user) || # local property for sidekiq only
49
+ fetch(:sidekiq_user) ||
50
+ properties.fetch(:run_as) || # global property across multiple capistrano gems
51
+ role.user
52
+ end
53
+ end
54
+
55
+ def expanded_bundle_path
56
+ backend.capture(:echo, SSHKit.config.command_map[:bundle]).strip
57
+ end
58
+
59
+ end
60
+ end
@@ -1,5 +1,7 @@
1
1
  module Capistrano
2
2
  class Sidekiq::Monit < Capistrano::Plugin
3
+ include Sidekiq::Helpers
4
+
3
5
  def set_defaults
4
6
  set_if_empty :monit_bin, '/usr/bin/monit'
5
7
  set_if_empty :sidekiq_monit_conf_dir, '/etc/monit/conf.d'
@@ -1,10 +1,13 @@
1
1
  module Capistrano
2
2
  class Sidekiq::Systemd < Capistrano::Plugin
3
+ include Sidekiq::Helpers
4
+
3
5
  def set_defaults
4
6
  set_if_empty :sidekiq_service_unit_name, 'sidekiq'
5
7
  set_if_empty :sidekiq_service_unit_user, :user # :system
6
8
  set_if_empty :sidekiq_enable_lingering, true
7
9
  set_if_empty :sidekiq_lingering_user, nil
10
+ set_if_empty :sidekiq_service_templates_path, 'config/deploy/templates'
8
11
  end
9
12
 
10
13
  def define_tasks
@@ -1,5 +1,7 @@
1
1
  module Capistrano
2
2
  class Sidekiq::Upstart < Capistrano::Plugin
3
+ include Sidekiq::Helpers
4
+
3
5
  def set_defaults
4
6
  set_if_empty :sidekiq_service_unit_name, 'sidekiq'
5
7
  end
@@ -1,3 +1,3 @@
1
1
  module Capistrano
2
- SidekiqVERSION = '2.0.0'
2
+ SidekiqVERSION = '2.1.0'
3
3
  end
@@ -25,6 +25,7 @@ module Capistrano
25
25
  end
26
26
  end
27
27
 
28
+ require_relative 'sidekiq/helpers'
28
29
  require_relative 'sidekiq/systemd'
29
30
  require_relative 'sidekiq/upstart'
30
31
  require_relative 'sidekiq/monit'
@@ -1,14 +1,4 @@
1
- namespace :load do
2
- task :defaults do
3
- set :sidekiq_monit_conf_dir, '/etc/monit/conf.d'
4
- set :sidekiq_monit_conf_file, -> { "#{sidekiq_service_name}.conf" }
5
- set :sidekiq_monit_use_sudo, true
6
- set :monit_bin, '/usr/bin/monit'
7
- set :sidekiq_monit_default_hooks, true
8
- set :sidekiq_monit_templates_path, 'config/deploy/templates'
9
- set :sidekiq_monit_group, nil
10
- end
11
- end
1
+ git_plugin = self
12
2
 
13
3
  namespace :deploy do
14
4
  before :starting, :check_sidekiq_monit_hooks do
@@ -20,7 +10,6 @@ end
20
10
 
21
11
  namespace :sidekiq do
22
12
  namespace :monit do
23
-
24
13
  task :add_default_hooks do
25
14
  before 'deploy:updating', 'sidekiq:monit:unmonitor'
26
15
  after 'deploy:published', 'sidekiq:monit:monitor'
@@ -30,24 +19,26 @@ namespace :sidekiq do
30
19
  task :config do
31
20
  on roles(fetch(:sidekiq_roles)) do |role|
32
21
  @role = role
33
- upload_sidekiq_template 'sidekiq_monit', "#{fetch(:tmp_dir)}/monit.conf", @role
22
+ git_plugin.upload_sidekiq_template 'sidekiq_monit', "#{fetch(:tmp_dir)}/monit.conf", @role
34
23
 
35
- mv_command = "mv #{fetch(:tmp_dir)}/monit.conf #{fetch(:sidekiq_monit_conf_dir)}/#{fetch(:sidekiq_monit_conf_file)}"
36
- sudo_if_needed mv_command
24
+ git_plugin.switch_user(role) do
25
+ mv_command = "mv #{fetch(:tmp_dir)}/monit.conf #{fetch(:sidekiq_monit_conf_dir)}/#{fetch(:sidekiq_monit_conf_file)}"
37
26
 
38
- sudo_if_needed "#{fetch(:monit_bin)} reload"
27
+ git_plugin.sudo_if_needed mv_command
28
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} reload"
29
+ end
39
30
  end
40
31
  end
41
32
 
42
33
  desc 'Monitor Sidekiq monit-service'
43
34
  task :monitor do
44
- on roles(fetch(:sidekiq_roles)) do
45
- fetch(:sidekiq_processes).times do |idx|
35
+ on roles(fetch(:sidekiq_roles)) do |role|
36
+ git_plugin.switch_user(role) do
46
37
  begin
47
- sudo_if_needed "#{fetch(:monit_bin)} monitor #{sidekiq_service_name(idx)}"
38
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} monitor #{git_plugin.sidekiq_service_name}"
48
39
  rescue
49
40
  invoke 'sidekiq:monit:config'
50
- sudo_if_needed "#{fetch(:monit_bin)} monitor #{sidekiq_service_name(idx)}"
41
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} monitor #{git_plugin.sidekiq_service_name}"
51
42
  end
52
43
  end
53
44
  end
@@ -55,114 +46,82 @@ namespace :sidekiq do
55
46
 
56
47
  desc 'Unmonitor Sidekiq monit-service'
57
48
  task :unmonitor do
58
- on roles(fetch(:sidekiq_roles)) do
59
- fetch(:sidekiq_processes).times do |idx|
49
+ on roles(fetch(:sidekiq_roles)) do |role|
50
+ git_plugin.switch_user(role) do
60
51
  begin
61
- sudo_if_needed "#{fetch(:monit_bin)} unmonitor #{sidekiq_service_name(idx)}"
52
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} unmonitor #{git_plugin.sidekiq_service_name}"
62
53
  rescue
63
54
  # no worries here
64
55
  end
65
56
  end
66
57
  end
67
58
  end
59
+ end
68
60
 
69
- desc 'Start Sidekiq monit-service'
70
- task :start do
71
- on roles(fetch(:sidekiq_roles)) do
72
- fetch(:sidekiq_processes).times do |idx|
73
- sudo_if_needed "#{fetch(:monit_bin)} start #{sidekiq_service_name(idx)}"
74
- end
75
- end
76
- end
77
-
78
- desc 'Stop Sidekiq monit-service'
79
- task :stop do
80
- on roles(fetch(:sidekiq_roles)) do
81
- fetch(:sidekiq_processes).times do |idx|
82
- sudo_if_needed "#{fetch(:monit_bin)} stop #{sidekiq_service_name(idx)}"
83
- end
84
- end
85
- end
86
-
87
- desc 'Restart Sidekiq monit-service'
88
- task :restart do
89
- on roles(fetch(:sidekiq_roles)) do
90
- fetch(:sidekiq_processes).times do |idx|
91
- sudo_if_needed"#{fetch(:monit_bin)} restart #{sidekiq_service_name(idx)}"
92
- end
93
- end
94
- end
95
-
96
- def sidekiq_service_name(index=nil)
97
- fetch(:sidekiq_service_name, "sidekiq_#{fetch(:application)}_#{fetch(:sidekiq_env)}") + (index ? "_#{index}" : '')
98
- end
99
-
100
- def sidekiq_config
101
- if fetch(:sidekiq_config)
102
- "--config #{fetch(:sidekiq_config)}"
103
- end
104
- end
105
-
106
- def sidekiq_concurrency
107
- if fetch(:sidekiq_concurrency)
108
- "--concurrency #{fetch(:sidekiq_concurrency)}"
61
+ desc 'Start Sidekiq monit-service'
62
+ task :start do
63
+ on roles(fetch(:sidekiq_roles)) do |role|
64
+ git_plugin.switch_user(role) do
65
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} start #{git_plugin.sidekiq_service_name}"
109
66
  end
110
67
  end
68
+ end
111
69
 
112
- def sidekiq_queues
113
- Array(fetch(:sidekiq_queue)).map do |queue|
114
- "--queue #{queue}"
115
- end.join(' ')
116
- end
117
-
118
- def sidekiq_logfile
119
- if fetch(:sidekiq_log)
120
- "--logfile #{fetch(:sidekiq_log)}"
70
+ desc 'Stop Sidekiq monit-service'
71
+ task :stop do
72
+ on roles(fetch(:sidekiq_roles)) do |role|
73
+ git_plugin.switch_user(role) do
74
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} stop #{git_plugin.sidekiq_service_name}"
121
75
  end
122
76
  end
77
+ end
123
78
 
124
- def sidekiq_require
125
- if fetch(:sidekiq_require)
126
- "--require #{fetch(:sidekiq_require)}"
127
- end
79
+ desc 'Restart Sidekiq monit-service'
80
+ task :restart do
81
+ on roles(fetch(:sidekiq_roles)) do |role|
82
+ git_plugin.sudo_if_needed "#{fetch(:monit_bin)} restart #{git_plugin.sidekiq_service_name}"
128
83
  end
84
+ end
129
85
 
130
- def sidekiq_options_per_process
131
- fetch(:sidekiq_options_per_process) || []
132
- end
86
+ def sidekiq_service_name
87
+ fetch(:sidekiq_service_name, "sidekiq_#{fetch(:application)}_#{fetch(:sidekiq_env)}")
88
+ end
133
89
 
134
- def sudo_if_needed(command)
135
- send(use_sudo? ? :sudo : :execute, command)
90
+ def sudo_if_needed(command)
91
+ if use_sudo?
92
+ backend.execute :sudo, command
93
+ else
94
+ backend.execute command
136
95
  end
96
+ end
137
97
 
138
- def use_sudo?
139
- fetch(:sidekiq_monit_use_sudo)
140
- end
98
+ def use_sudo?
99
+ fetch(:sidekiq_monit_use_sudo)
100
+ end
141
101
 
142
- def upload_sidekiq_template(from, to, role)
143
- template = sidekiq_template(from, role)
144
- upload!(StringIO.new(ERB.new(template).result(binding)), to)
145
- end
102
+ def upload_sidekiq_template(from, to, role)
103
+ template = sidekiq_template(from, role)
104
+ backend.upload!(StringIO.new(ERB.new(template).result(binding)), to)
105
+ end
146
106
 
147
- def sidekiq_template(name, role)
148
- local_template_directory = fetch(:sidekiq_monit_templates_path)
107
+ def sidekiq_template(name, role)
108
+ local_template_directory = fetch(:sidekiq_monit_templates_path)
149
109
 
150
- search_paths = [
151
- "#{name}-#{role.hostname}-#{fetch(:stage)}.erb",
152
- "#{name}-#{role.hostname}.erb",
153
- "#{name}-#{fetch(:stage)}.erb",
154
- "#{name}.erb"
155
- ].map { |filename| File.join(local_template_directory, filename) }
110
+ search_paths = [
111
+ "#{name}-#{role.hostname}-#{fetch(:stage)}.erb",
112
+ "#{name}-#{role.hostname}.erb",
113
+ "#{name}-#{fetch(:stage)}.erb",
114
+ "#{name}.erb"
115
+ ].map { |filename| File.join(local_template_directory, filename) }
156
116
 
157
- global_search_path = File.expand_path(
158
- File.join(*%w[.. .. .. generators capistrano sidekiq monit templates], "#{name}.conf.erb"),
159
- __FILE__
160
- )
117
+ global_search_path = File.expand_path(
118
+ File.join(*%w[.. .. .. generators capistrano sidekiq monit templates], "#{name}.conf.erb"),
119
+ __FILE__
120
+ )
161
121
 
162
- search_paths << global_search_path
122
+ search_paths << global_search_path
163
123
 
164
- template_path = search_paths.detect { |path| File.file?(path) }
165
- File.read(template_path)
166
- end
124
+ template_path = search_paths.detect { |path| File.file?(path) }
125
+ File.read(template_path)
167
126
  end
168
127
  end
@@ -6,15 +6,9 @@ end
6
6
 
7
7
  namespace :sidekiq do
8
8
  task :add_default_hooks do
9
- after 'deploy:starting', 'sidekiq:quiet'
9
+ after 'deploy:starting', 'sidekiq:quiet' if Rake::Task.task_defined?('sidekiq:quiet')
10
10
  after 'deploy:updated', 'sidekiq:stop'
11
11
  after 'deploy:published', 'sidekiq:start'
12
12
  after 'deploy:failed', 'sidekiq:restart'
13
13
  end
14
-
15
- desc 'Restart sidekiq'
16
- task :restart do
17
- invoke! 'sidekiq:stop'
18
- invoke! 'sidekiq:start'
19
- end
20
14
  end
@@ -1,41 +1,73 @@
1
1
  git_plugin = self
2
2
 
3
3
  namespace :sidekiq do
4
- desc 'Quiet sidekiq (stop fetching new tasks from Redis)'
5
- task :quiet do
6
- on roles fetch(:sidekiq_roles) do |role|
7
- git_plugin.switch_user(role) do
8
- if fetch(:sidekiq_service_unit_user) == :system
9
- execute :sudo, :systemctl, "reload", fetch(:sidekiq_service_unit_name), raise_on_non_zero_exit: false
10
- else
11
- execute :systemctl, "--user", "reload", fetch(:sidekiq_service_unit_name), raise_on_non_zero_exit: false
4
+
5
+ standard_actions = {
6
+ start: 'Start Sidekiq',
7
+ stop: 'Stop Sidekiq (graceful shutdown within timeout, put unfinished tasks back to Redis)',
8
+ status: 'Get Sidekiq Status'
9
+ }
10
+ standard_actions.each do |command, description|
11
+ desc description
12
+ task command do
13
+ on roles fetch(:sidekiq_roles) do |role|
14
+ git_plugin.switch_user(role) do
15
+ git_plugin.systemctl_command(command)
12
16
  end
13
17
  end
14
18
  end
15
19
  end
16
20
 
17
- desc 'Stop sidekiq (graceful shutdown within timeout, put unfinished tasks back to Redis)'
18
- task :stop do
21
+ desc 'Restart Sidekiq (Quiet, Wait till workers finish or 30 seconds, Stop, Start)'
22
+ task :restart do
19
23
  on roles fetch(:sidekiq_roles) do |role|
20
24
  git_plugin.switch_user(role) do
21
- if fetch(:sidekiq_service_unit_user) == :system
22
- execute :sudo, :systemctl, "stop", fetch(:sidekiq_service_unit_name)
23
- else
24
- execute :systemctl, "--user", "stop", fetch(:sidekiq_service_unit_name)
25
+ git_plugin.quiet_sidekiq
26
+ git_plugin.process_block do |process|
27
+ start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
28
+ running = nil
29
+
30
+ # get running workers
31
+ while (running.nil? || running > 0) && git_plugin.duration(start_time) < 30 do
32
+ command_args =
33
+ if fetch(:sidekiq_service_unit_user) == :system
34
+ [:sudo, 'systemd-cgls']
35
+ else
36
+ ['systemd-clgs', '--user']
37
+ end
38
+ # need to pipe through tr -cd... to strip out systemd colors or you
39
+ # get log error messages for non UTF-8 characters.
40
+ command_args.push(
41
+ '-u', "#{git_plugin.sidekiq_service_unit_name(process: process)}.service",
42
+ '|', 'tr -cd \'\11\12\15\40-\176\''
43
+ )
44
+ status = capture(*command_args, raise_on_non_zero_exit: false)
45
+ status_match = status.match(/\[(?<running>\d+) of (?<total>\d+) busy\]/)
46
+ break unless status_match
47
+
48
+ running = status_match[:running]&.to_i
49
+
50
+ colors = SSHKit::Color.new($stdout)
51
+ if running.zero?
52
+ info colors.colorize("✔ Process ##{process}: No running workers. Shutting down for restart!", :green)
53
+ else
54
+ info colors.colorize("⧗ Process ##{process}: Waiting for #{running} workers.", :yellow)
55
+ sleep(1)
56
+ end
57
+ end
58
+
59
+ git_plugin.systemctl_command(:stop, process: process)
60
+ git_plugin.systemctl_command(:start, process: process)
25
61
  end
26
62
  end
27
63
  end
28
64
  end
29
65
 
30
- desc 'Start sidekiq'
31
- task :start do
66
+ desc 'Quiet Sidekiq (stop fetching new tasks from Redis)'
67
+ task :quiet do
32
68
  on roles fetch(:sidekiq_roles) do |role|
33
69
  git_plugin.switch_user(role) do
34
- if fetch(:sidekiq_service_unit_user) == :system
35
- execute :sudo, :systemctl, 'start', fetch(:sidekiq_service_unit_name)
36
- else
37
- execute :systemctl, '--user', 'start', fetch(:sidekiq_service_unit_name)
38
- end
70
+ git_plugin.quiet_sidekiq
39
71
  end
40
72
  end
41
73
  end
@@ -45,26 +77,35 @@ namespace :sidekiq do
45
77
  on roles fetch(:sidekiq_roles) do |role|
46
78
  git_plugin.switch_user(role) do
47
79
  git_plugin.create_systemd_template
48
- if fetch(:sidekiq_service_unit_user) == :system
49
- execute :sudo, :systemctl, "enable", fetch(:sidekiq_service_unit_name)
50
- else
51
- execute :systemctl, "--user", "enable", fetch(:sidekiq_service_unit_name)
52
- execute :loginctl, "enable-linger", fetch(:sidekiq_lingering_user) if fetch(:sidekiq_enable_lingering)
80
+ if git_plugin.config_per_process?
81
+ git_plugin.process_block do |process|
82
+ git_plugin.create_systemd_config_symlink(process)
83
+ end
84
+ end
85
+ git_plugin.systemctl_command(:enable)
86
+
87
+ if fetch(:sidekiq_service_unit_user) != :system && fetch(:sidekiq_enable_lingering)
88
+ execute :loginctl, "enable-linger", fetch(:sidekiq_lingering_user)
53
89
  end
54
90
  end
55
91
  end
56
92
  end
57
93
 
58
- desc 'UnInstall systemd sidekiq service'
94
+ desc 'Uninstall systemd sidekiq service'
59
95
  task :uninstall do
60
96
  on roles fetch(:sidekiq_roles) do |role|
61
97
  git_plugin.switch_user(role) do
62
- if fetch(:sidekiq_service_unit_user) == :system
63
- execute :sudo, :systemctl, "disable", fetch(:sidekiq_service_unit_name)
64
- else
65
- execute :systemctl, "--user", "disable", fetch(:sidekiq_service_unit_name)
98
+ git_plugin.systemctl_command(:stop)
99
+ git_plugin.systemctl_command(:disable)
100
+ if git_plugin.config_per_process?
101
+ git_plugin.process_block do |process|
102
+ git_plugin.delete_systemd_config_symlink(process)
103
+ end
66
104
  end
67
- execute :rm, '-f', File.join(fetch(:service_unit_path, fetch_systemd_unit_path), fetch(:sidekiq_service_unit_name))
105
+ execute :sudo, :rm, '-f', File.join(
106
+ fetch(:service_unit_path, git_plugin.fetch_systemd_unit_path),
107
+ git_plugin.sidekiq_service_file_name
108
+ )
68
109
  end
69
110
  end
70
111
  end
@@ -87,7 +128,10 @@ namespace :sidekiq do
87
128
  end
88
129
 
89
130
  def compiled_template
131
+ local_template_directory = fetch(:sidekiq_service_templates_path)
90
132
  search_paths = [
133
+ File.join(local_template_directory, "#{fetch(:sidekiq_service_unit_name)}.service.capistrano.erb"),
134
+ File.join(local_template_directory, 'sidekiq.service.capistrano.erb'),
91
135
  File.expand_path(
92
136
  File.join(*%w[.. .. .. generators capistrano sidekiq systemd templates sidekiq.service.capistrano.erb]),
93
137
  __FILE__
@@ -101,23 +145,78 @@ namespace :sidekiq do
101
145
  def create_systemd_template
102
146
  ctemplate = compiled_template
103
147
  systemd_path = fetch(:service_unit_path, fetch_systemd_unit_path)
148
+ systemd_file_name = File.join(systemd_path, sidekiq_service_file_name)
104
149
 
105
150
  if fetch(:sidekiq_service_unit_user) == :user
106
151
  backend.execute :mkdir, "-p", systemd_path
107
152
  end
108
- backend.upload!(
109
- StringIO.new(ctemplate),
110
- "/tmp/#{fetch :sidekiq_service_unit_name}.service"
111
- )
153
+
154
+ temp_file_name = File.join('/tmp', sidekiq_service_file_name)
155
+ backend.upload!(StringIO.new(ctemplate), temp_file_name)
112
156
  if fetch(:sidekiq_service_unit_user) == :system
113
- backend.execute :sudo, :mv, "/tmp/#{fetch :sidekiq_service_unit_name}.service", "#{systemd_path}/#{fetch :sidekiq_service_unit_name}.service"
157
+ backend.execute :sudo, :mv, temp_file_name, systemd_file_name
114
158
  backend.execute :sudo, :systemctl, "daemon-reload"
115
159
  else
116
- backend.execute :mv, "/tmp/#{fetch :sidekiq_service_unit_name}.service", "#{systemd_path}/#{fetch :sidekiq_service_unit_name}.service"
160
+ backend.execute :mv, temp_file_name, systemd_file_name
117
161
  backend.execute :systemctl, "--user", "daemon-reload"
118
162
  end
119
163
  end
120
164
 
165
+ def create_systemd_config_symlink(process)
166
+ config = fetch(:sidekiq_config)
167
+ return unless config
168
+
169
+ process_config = config[process - 1]
170
+ if process_config.nil?
171
+ backend.error(
172
+ "No configuration for Process ##{process} found. "\
173
+ 'Please make sure you have 1 item in :sidekiq_config for each process.'
174
+ )
175
+ exit 1
176
+ end
177
+
178
+ base_path = fetch(:deploy_to)
179
+ config_link_base_path = File.join(base_path, 'shared', 'sidekiq_systemd')
180
+ config_link_path = File.join(
181
+ config_link_base_path, sidekiq_systemd_config_name(process)
182
+ )
183
+ process_config_path = File.join(base_path, 'current', process_config)
184
+
185
+ backend.execute :mkdir, '-p', config_link_base_path
186
+ backend.execute :ln, '-sf', process_config_path, config_link_path
187
+ end
188
+
189
+ def delete_systemd_config_symlink(process)
190
+ config_link_path = File.join(
191
+ fetch(:deploy_to), 'shared', 'sidekiq_systemd',
192
+ sidekiq_systemd_config_name(process)
193
+ )
194
+ backend.execute :rm, config_link_path, raise_on_non_zero_exit: false
195
+ end
196
+
197
+ def systemctl_command(*args, process: nil)
198
+ execute_array =
199
+ if fetch(:sidekiq_service_unit_user) == :system
200
+ [:sudo, :systemctl]
201
+ else
202
+ [:systemctl, '--user']
203
+ end
204
+
205
+ if process
206
+ execute_array.push(
207
+ *args, sidekiq_service_unit_name(process: process)
208
+ ).flatten
209
+ backend.execute(*execute_array, raise_on_non_zero_exit: false)
210
+ else
211
+ execute_array.push(*args, sidekiq_service_unit_name).flatten
212
+ backend.execute(*execute_array, raise_on_non_zero_exit: false)
213
+ end
214
+ end
215
+
216
+ def quiet_sidekiq
217
+ systemctl_command(:kill, '-s', :TSTP)
218
+ end
219
+
121
220
  def switch_user(role)
122
221
  su_user = sidekiq_user
123
222
  if su_user != role.user
@@ -132,4 +231,70 @@ namespace :sidekiq do
132
231
  def sidekiq_user
133
232
  fetch(:sidekiq_user, fetch(:run_as))
134
233
  end
234
+
235
+ def sidekiq_config
236
+ config = fetch(:sidekiq_config)
237
+ return unless config
238
+
239
+ if config_per_process?
240
+ config = File.join(
241
+ fetch(:deploy_to), 'shared', 'sidekiq_systemd',
242
+ sidekiq_systemd_config_name
243
+ )
244
+ "--config #{config}"
245
+ else
246
+ "--config #{config}"
247
+ end
248
+ end
249
+
250
+ def sidekiq_concurrency
251
+ if fetch(:sidekiq_concurrency)
252
+ "--concurrency #{fetch(:sidekiq_concurrency)}"
253
+ end
254
+ end
255
+
256
+ def sidekiq_processes
257
+ fetch(:sidekiq_processes, 1)
258
+ end
259
+
260
+ def sidekiq_queues
261
+ Array(fetch(:sidekiq_queue)).map do |queue|
262
+ "--queue #{queue}"
263
+ end.join(' ')
264
+ end
265
+
266
+ def sidekiq_service_file_name
267
+ "#{fetch(:sidekiq_service_unit_name)}@.service"
268
+ end
269
+
270
+ def sidekiq_service_unit_name(process: nil)
271
+ if process
272
+ "#{fetch(:sidekiq_service_unit_name)}@#{process}"
273
+ else
274
+ "#{fetch(:sidekiq_service_unit_name)}@{1..#{sidekiq_processes}}"
275
+ end
276
+ end
277
+
278
+ # process = 1 | sidekiq_systemd_1.yaml
279
+ # process = nil | sidekiq_systemd_%i.yaml
280
+ def sidekiq_systemd_config_name(process = nil)
281
+ file_name = 'sidekiq_systemd_'
282
+ file_name << (process&.to_s || '%i')
283
+ "#{file_name}.yaml"
284
+ end
285
+
286
+ def config_per_process?
287
+ fetch(:sidekiq_config).is_a?(Array)
288
+ end
289
+
290
+ def process_block
291
+ (1..sidekiq_processes).each do |process|
292
+ yield(process)
293
+ end
294
+ end
295
+
296
+ def duration(start_time)
297
+ Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time
298
+ end
299
+
135
300
  end
@@ -98,49 +98,8 @@ namespace :sidekiq do
98
98
  end
99
99
  end
100
100
 
101
- def switch_user(role)
102
- su_user = sidekiq_user(role)
103
- if su_user == role.user
104
- yield
105
- else
106
- as su_user do
107
- yield
108
- end
109
- end
110
- end
111
-
112
- def sidekiq_user(role = nil)
113
- if role.nil?
114
- fetch(:sidekiq_user)
115
- else
116
- properties = role.properties
117
- properties.fetch(:sidekiq_user) || # local property for sidekiq only
118
- fetch(:sidekiq_user) ||
119
- properties.fetch(:run_as) || # global property across multiple capistrano gems
120
- role.user
121
- end
122
- end
123
-
124
101
  def num_workers
125
102
  fetch(:sidekiq_upstart_num_workers, nil)
126
103
  end
127
104
 
128
- def sidekiq_config
129
- if fetch(:sidekiq_config)
130
- "--config #{fetch(:sidekiq_config)}"
131
- end
132
- end
133
-
134
- def sidekiq_concurrency
135
- if fetch(:sidekiq_concurrency)
136
- "--concurrency #{fetch(:sidekiq_concurrency)}"
137
- end
138
- end
139
-
140
- def sidekiq_queues
141
- Array(fetch(:sidekiq_queue)).map do |queue|
142
- "--queue #{queue}"
143
- end.join(' ')
144
- end
145
-
146
105
  end
@@ -1,10 +1,6 @@
1
1
  # Monit configuration for Sidekiq : <%= fetch(:application) %>
2
- <% pid_files.each_with_index do |pid_file, idx| %>
3
- check process <%= sidekiq_service_name(idx) %>
4
- with pidfile "<%= pid_file %>"
5
- start program = "/bin/su - <%= sidekiq_user(@role) %> -c 'cd <%= current_path %> && <%= SSHKit.config.command_map[:sidekiq] %> <%= sidekiq_config %> --index <%= idx %> --pidfile <%= pid_file %> --environment <%= fetch(:sidekiq_env) %> <%= sidekiq_concurrency %> <%= sidekiq_logfile %> <%= sidekiq_require %> <%= sidekiq_queues %> <%= sidekiq_options_per_process[idx] %> -d'" with timeout 30 seconds
6
-
7
- stop program = "/bin/su - <%= sidekiq_user(@role) %> -c 'cd <%= current_path %> && <%= SSHKit.config.command_map[:sidekiqctl] %> stop <%= pid_file %>'" with timeout <%= fetch(:sidekiq_timeout).to_i + 10 %> seconds
2
+ check process <%= sidekiq_service_name %>
3
+ matching 'sidekiq .* <%= fetch(:full_app_name) %>'
4
+ start program = "/bin/su - <%= sidekiq_user(role) %> -c 'cd <%= current_path %> && <%= SSHKit.config.command_map[:bundle] %> exec sidekiq -e <%= fetch(:sidekiq_env) %> <%= sidekiq_config %> <%= sidekiq_concurrency %> <%= sidekiq_require %> <%= sidekiq_queues %> <%= sidekiq_logfile ? ">> #{sidekiq_logfile} 2>&1" : nil %> &'" with timeout 30 seconds
5
+ stop program = "/bin/su - <%= sidekiq_user(role) %> -c 'ps -ax | grep "<%= "sidekiq .* #{fetch(:full_app_name)}" %>" | grep -v grep | awk "{print \$1}" | xargs --no-run-if-empty kill'" with timeout <%= fetch(:sidekiq_timeout).to_i + 10 %> seconds
8
6
  group <%= fetch(:sidekiq_monit_group) || fetch(:application) %>-sidekiq
9
-
10
- <% end %>
@@ -1,25 +1,69 @@
1
+ # Source: https://github.com/mperham/sidekiq/blob/master/examples/systemd/sidekiq.service
2
+ #
3
+ # This file tells systemd how to run Sidekiq as a 24/7 long-running daemon.
4
+ #
5
+ # Customize this file based on your bundler location, app directory, etc.
6
+ # Customize and copy this into /usr/lib/systemd/system (CentOS) or /lib/systemd/system (Ubuntu).
7
+ # Then run:
8
+ # - systemctl enable sidekiq
9
+ # - systemctl {start,stop,restart} sidekiq
10
+ #
11
+ # This file corresponds to a single Sidekiq process. Add multiple copies
12
+ # to run multiple processes (sidekiq-1, sidekiq-2, etc).
13
+ #
14
+ # Use `journalctl -u sidekiq -rn 100` to view the last 100 lines of log output.
15
+ #
1
16
  [Unit]
2
17
  Description=sidekiq for <%= "#{fetch(:application)} (#{fetch(:stage)})" %>
18
+ # start us only once the network and logging subsystems are available,
19
+ # consider adding redis-server.service if Redis is local and systemd-managed.
3
20
  After=syslog.target network.target
4
21
 
22
+ # See these pages for lots of options:
23
+ #
24
+ # https://www.freedesktop.org/software/systemd/man/systemd.service.html
25
+ # https://www.freedesktop.org/software/systemd/man/systemd.exec.html
26
+ #
27
+ # THOSE PAGES ARE CRITICAL FOR ANY LINUX DEVOPS WORK; read them multiple
28
+ # times! systemd is a critical tool for all developers to know and understand.
29
+ #
5
30
  [Service]
6
- Type=simple
31
+ #
32
+ # !!!! !!!! !!!!
33
+ #
34
+ # As of v6.0.6, Sidekiq automatically supports systemd's `Type=notify` and watchdog service
35
+ # monitoring. If you are using an earlier version of Sidekiq, change this to `Type=simple`
36
+ # and remove the `WatchdogSec` line.
37
+ #
38
+ # !!!! !!!! !!!!
39
+ #
40
+ Type=notify
41
+ # If your Sidekiq process locks up, systemd's watchdog will restart it within seconds.
42
+ WatchdogSec=10
43
+
7
44
  WorkingDirectory=<%= File.join(fetch(:deploy_to), 'current') %>
8
- ExecStart=<%= SSHKit.config.command_map[:bundle] %> exec sidekiq -e <%= fetch(:sidekiq_env) %>
9
- ExecReload=/bin/kill -TSTP $MAINPID
10
- ExecStop=/bin/kill -TERM $MAINPID
11
- <%="StandardOutput=append:#{fetch(:sidekiq_log)}" if fetch(:sidekiq_log) %>
12
- <%="StandardError=append:#{fetch(:sidekiq_error_log)}" if fetch(:sidekiq_error_log) %>
45
+ ExecStart=<%= expanded_bundle_path %> exec sidekiq -e <%= fetch(:sidekiq_env) %> <%= sidekiq_config %> <%= sidekiq_concurrency %> <%= sidekiq_queues %>
46
+
47
+ # Use `systemctl kill -s TSTP sidekiq` to quiet the Sidekiq process
48
+
13
49
  <%="User=#{sidekiq_user}" if sidekiq_user %>
14
- <%="EnvironmentFile=#{fetch(:sidekiq_service_unit_env_file)}" if fetch(:sidekiq_service_unit_env_file) %>
50
+ UMask=0002
15
51
 
16
- <% fetch(:sidekiq_service_unit_env_vars, []).each do |environment_variable| %>
17
- <%="Environment=#{environment_variable}" %>
18
- <% end %>
52
+ <%="EnvironmentFile=#{File.join(fetch(:deploy_to), 'current')}/#{fetch(:sidekiq_service_unit_env_file)}" if fetch(:sidekiq_service_unit_env_file) %>
19
53
 
54
+ # Greatly reduce Ruby memory fragmentation and heap usage
55
+ # https://www.mikeperham.com/2018/04/25/taming-rails-memory-bloat/
56
+ Environment=MALLOC_ARENA_MAX=2
57
+
58
+ # if we crash, restart
20
59
  RestartSec=1
21
60
  Restart=on-failure
22
61
 
62
+ # output goes to /var/log/syslog (Ubuntu) or /var/log/messages (CentOS)
63
+ <%="StandardOutput=append:#{fetch(:sidekiq_log)}" if fetch(:sidekiq_log) %>
64
+ <%="StandardError=append:#{fetch(:sidekiq_error_log)}" if fetch(:sidekiq_error_log) %>
65
+
66
+ # This will default to "bundler" if we don't specify it
23
67
  SyslogIdentifier=sidekiq
24
68
 
25
69
  [Install]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano-sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-19 00:00:00.000000000 Z
11
+ date: 2022-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capistrano
@@ -70,6 +70,7 @@ files:
70
70
  - capistrano-sidekiq.gemspec
71
71
  - lib/capistrano-sidekiq.rb
72
72
  - lib/capistrano/sidekiq.rb
73
+ - lib/capistrano/sidekiq/helpers.rb
73
74
  - lib/capistrano/sidekiq/monit.rb
74
75
  - lib/capistrano/sidekiq/systemd.rb
75
76
  - lib/capistrano/sidekiq/upstart.rb
@@ -102,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
103
  - !ruby/object:Gem::Version
103
104
  version: '0'
104
105
  requirements: []
105
- rubygems_version: 3.0.3
106
+ rubygems_version: 3.2.22
106
107
  signing_key:
107
108
  specification_version: 4
108
109
  summary: Sidekiq integration for Capistrano