capistrano-sidekiq 2.0.0 → 2.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
  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