Module: ProcessBot::Capistrano::SidekiqHelpers

Included in:
Sidekiq
Defined in:
lib/process_bot/capistrano/sidekiq_helpers.rb

Overview

rubocop:disable Metrics/ModuleLength

Instance Method Summary collapse

Instance Method Details

#expanded_bundle_pathObject



139
140
141
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 139

def expanded_bundle_path
  backend.capture(:echo, SSHKit.config.command_map[:bundle]).strip
end

#missing_sidekiq_indexes(desired_processes, active_indexes) ⇒ Object



121
122
123
124
125
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 121

def missing_sidekiq_indexes(desired_processes, active_indexes)
  desired = desired_processes.to_i
  desired_indexes = (0...desired).to_a
  desired_indexes - active_indexes
end

#parse_process_bot_process_from_ps(processes_output) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 83

def parse_process_bot_process_from_ps(processes_output)
  processes = []
  processes_output.scan(/^\s*(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+ProcessBot (\{([^\n]+?)\})$/).each do |process_output|
    process_bot_data = JSON.parse(process_output[4])
    process_bot_pid = process_output[0]
    process_bot_data["process_bot_pid"] = process_bot_pid

    processes << process_bot_data
  end

  processes
end

#process_bot_command(process_bot_data, command, args = {}) ⇒ Object

rubocop:disable Metrics/AbcSize



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 35

def process_bot_command(process_bot_data, command, args = {}) # rubocop:disable Metrics/AbcSize
  raise "No port in process bot data? #{process_bot_data}" unless process_bot_data["port"]

  mode = "exec"

  if mode == "runner"
    args = {command: command, port: process_bot_data.fetch("port")}
    args["log"] = fetch(:process_bot_log) unless fetch(:process_bot_log).nil?

    escaped_args = JSON.generate(args).gsub("\"", "\\\"")
    rails_runner_command = "require 'process_bot'; ProcessBot::Process.new(ProcessBot::Options.from_args(#{escaped_args})).execute!"

    backend_command = "cd #{release_path} && " \
      "#{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec rails runner \"#{rails_runner_command}\""
  elsif mode == "exec"
    backend_command = "cd #{release_path} && " \
      "#{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot " \
      "--command #{command} " \
      "--port #{process_bot_data.fetch("port")}"

    backend_command << " --log #{fetch(:process_bot_log)}" unless fetch(:process_bot_log).nil?

    args.each do |key, value|
      backend_command << "#{key} #{value}"
    end

  else
    raise "Unknown mode: #{mode}"
  end

  backend.execute backend_command
end

#process_bot_sidekiq_index(process_bot_data) ⇒ Object



96
97
98
99
100
101
102
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 96

def process_bot_sidekiq_index(process_bot_data)
  process_bot_id = process_bot_data["id"].to_s
  match = process_bot_id.match(/-(\d+)\z/)
  return nil unless match

  match[1].to_i
end

#running_process_bot_processesObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 68

def running_process_bot_processes
  sidekiq_app_name = fetch(:sidekiq_app_name, fetch(:application))
  raise "No :sidekiq_app_name was set" unless sidekiq_app_name

  begin
    processes_output = backend.capture("ps a | grep ProcessBot | grep sidekiq | grep -v '/usr/bin/SCREEN' | grep '#{Regexp.escape(sidekiq_app_name)}'")
  rescue SSHKit::Command::Failed
    # Fails when output is empty (when no processes found through grep)
    puts "No ProcessBot Sidekiq processes found"
    return []
  end

  parse_process_bot_process_from_ps(processes_output)
end

#sidekiq_command_graceful?(command) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
107
108
109
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 104

def sidekiq_command_graceful?(command)
  return false unless command

  normalized_command = command.to_s.downcase
  normalized_command.include?("stopping") || normalized_command.include?("quiet")
end

#sidekiq_concurrencyObject



12
13
14
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 12

def sidekiq_concurrency
  "--concurrency #{fetch(:sidekiq_concurrency)}" if fetch(:sidekiq_concurrency)
end

#sidekiq_configObject



8
9
10
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 8

def sidekiq_config
  "--config #{fetch(:sidekiq_config)}" if fetch(:sidekiq_config)
end

#sidekiq_logfileObject



22
23
24
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 22

def sidekiq_logfile
  fetch(:sidekiq_log)
end

#sidekiq_process_graceful?(process_bot_data) ⇒ Boolean

Returns:

  • (Boolean)


111
112
113
114
115
116
117
118
119
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 111

def sidekiq_process_graceful?(process_bot_data)
  sidekiq_pid = process_bot_data["pid"]
  return false unless sidekiq_pid

  command = backend.capture(:ps, "-o", "command=", "-p", sidekiq_pid.to_s).strip
  sidekiq_command_graceful?(command)
rescue SSHKit::Command::Failed
  false
end

#sidekiq_queuesObject



16
17
18
19
20
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 16

def sidekiq_queues
  Array(fetch(:sidekiq_queue)).map do |queue|
    "--queue #{queue}"
  end.join(" ")
end

#sidekiq_requireObject



4
5
6
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 4

def sidekiq_require
  "--require #{fetch(:sidekiq_require)}" if fetch(:sidekiq_require)
end

#sidekiq_user(role = nil) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 127

def sidekiq_user(role = nil)
  if role.nil?
    fetch(:sidekiq_user)
  else
    properties = role.properties
    properties.fetch(:sidekiq_user) || # local property for sidekiq only
      fetch(:sidekiq_user) ||
      properties.fetch(:run_as) || # global property across multiple capistrano gems
      role.user
  end
end

#start_sidekiq(idx = 0) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 143

def start_sidekiq(idx = 0) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
  releases = backend.capture(:ls, "-x", releases_path).split
  releases << release_timestamp.to_s if release_timestamp
  releases.uniq

  latest_release_version = releases.last
  raise "Invalid release timestamp: #{release_timestamp}" unless latest_release_version

  args = [
    "--command", "start",
    "--id", "sidekiq-#{latest_release_version}-#{idx}",
    "--application", fetch(:sidekiq_app_name, fetch(:application)),
    "--handler", "sidekiq",
    "--bundle-prefix", SSHKit.config.command_map.prefix[:bundle].join(" "),
    "--sidekiq-environment", fetch(:sidekiq_env),
    "--port", idx + 7050,
    "--release-path", release_path
  ]

  # Use screen for logging everything which is why this is disabled
  # args += ["--log-file-path", fetch(:sidekiq_log)] if fetch(:sidekiq_log)

  args += ["--sidekiq-require", fetch(:sidekiq_require)] if fetch(:sidekiq_require)
  args += ["--sidekiq-tag", fetch(:sidekiq_tag)] if fetch(:sidekiq_tag)
  args += ["--sidekiq-queues", Array(fetch(:sidekiq_queue)).join(",")] if fetch(:sidekiq_queue)
  args += ["--sidekiq-config", fetch(:sidekiq_config)] if fetch(:sidekiq_config)
  args += ["--sidekiq-concurrency", fetch(:sidekiq_concurrency)] if fetch(:sidekiq_concurrency)
  args += ["--sidekiq-restart-overlap", fetch(:sidekiq_restart_overlap)] unless fetch(:sidekiq_restart_overlap, nil).nil?
  if (process_options = fetch(:sidekiq_options_per_process))
    args += process_options[idx]
  end
  args += fetch(:sidekiq_options) if fetch(:sidekiq_options)

  screen_args = ["-dmS process-bot--sidekiq--#{idx}-#{latest_release_version}"]

  if (process_bot_sidekiq_log = fetch(:process_bot_sidekig_log))
    screen_args << "-L -Logfile #{process_bot_sidekiq_log}_#{latest_release_version}_#{idx}.log"
  elsif fetch(:sidekiq_log)
    screen_args << "-L -Logfile #{fetch(:sidekiq_log)}"
  end

  process_bot_args = args.compact.map { |arg| "\"#{arg}\"" }

  command = "/usr/bin/screen #{screen_args.join(" ")} " \
    "bash -c 'cd #{release_path} && exec #{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot #{process_bot_args.join(" ")}'"

  puts "WARNING: A known bug prevents Sidekiq from starting when pty is set (which it is)" if fetch(:pty)
  puts "ProcessBot Sidekiq command: #{command}"

  backend.execute command
end

#switch_user(role) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/process_bot/capistrano/sidekiq_helpers.rb', line 26

def switch_user(role, &)
  su_user = sidekiq_user(role)
  if su_user == role.user
    yield
  else
    as(su_user, &)
  end
end