Commit 5945d4db by Karl Kloppenborg Committed by GitHub

Merge pull request #14 from Tensho/sneakers-monit-templates-path

Mimicry capistrano-sidekiq
parents 2a0b7e85 d0580808
......@@ -2,7 +2,10 @@
## [Unreleased]
- Add CHANGELOG @Tensho
## 0.1.0
- Mimicry capistrano-sidekiq @Tensho
- Add sneakers_monit_templates_path @Tensho
## 0.0.2
......
......@@ -6,36 +6,44 @@
Add this line to your application's Gemfile:
```ruby
gem 'capistrano-sneakers'
```
gem 'capistrano-sneakers', github: 'inventionlabsSydney/capistrano-sneakers'
And then execute:
$ bundle
Or install it yourself as:
$ gem install capistrano-sneakers
## Usage
## Usage
```ruby
# Capfile
require 'capistrano/sneakers'
# Include monit tasks
# require 'capistrano/sneakers/monit'
require 'capistrano/sneakers/monit' # optional, to require monit tasks
```
Configurable options, shown here with defaults:
```ruby
:sneakers_default_hooks => true
:sneakers_pid => File.join(shared_path, 'tmp', 'pids', 'sneakers.pid') # ensure this path exists in production before deploying
:sneakers_env => fetch(:rack_env, fetch(:rails_env, fetch(:stage)))
:sneakers_log => File.join(shared_path, 'log', 'sneakers.log')
:sneakers_roles => :app
:sneakers_processes => 1
# sneakers monit
:sneakers_monit_conf_dir => '/etc/monit/conf.d'
:sneakers_monit_use_sudo => true
:monit_bin => '/usr/bin/monit'
:sneakers_monit_templates_path => 'config/deploy/templates'
```
## Contributors
- [Andrew Babichev](https://github.com/Tensho)
- [NaixSpirit](https://github.com/NaixSpirit)
## Contributing
1. Fork it ( https://github.com/NaixSpirit/capistrano-sneakers/fork )
1. Fork it (https://github.com/inventionlabsSydney/capistrano-sneakers)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
......
......@@ -5,11 +5,11 @@ require 'capistrano/sneakers/version'
Gem::Specification.new do |spec|
spec.name = "capistrano-sneakers"
spec.version = Capistrano::Sneakers::VERSION
spec.authors = ["Spirit"]
spec.email = ["neverlandxy.naix@gmail.com"]
spec.summary = %q{Sneakers integration only for Capistrano3}
spec.description = %q{Sneakers integration only for Capistrano3}
spec.homepage = "https://github.com/NaixSpirit/capistrano-sneakers"
spec.authors = ["Karl Kloppenborg, Andrew Babichev, NaixSpirit"]
spec.email = ["karl@hyperconnect.com.au", "andrew.babichev@gmail.com", "neverlandxy.naix@gmail.com"]
spec.summary = %q{Sneakers integration for Capistrano}
spec.description = %q{Sneakers integration for Capistrano}
spec.homepage = "https://github.com/inventionlabsSydney/capistrano-sneakers"
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0")
......
module Capistrano
module Sneakers
VERSION = "0.0.2"
VERSION = "0.1.0"
end
end
namespace :load do
task :defaults do
set :sneakers_monit_conf_dir, -> { "/etc/monit/conf.d/#{sneakers_monit_service_name}.conf" }
set :monit_bin, '/usr/bin/monit'
set :sneakers_monit_default_hooks, true
set :sneakers_monit_conf_dir, -> { '/etc/monit/conf.d' }
set :sneakers_monit_use_sudo, true
set :sneakers_monit_bin, '/usr/bin/monit'
set :sneakers_monit_templates_path, 'config/deploy/templates'
end
end
namespace :deploy do
before :starting, :check_sidekiq_monit_hooks do
if fetch(:sneakers_default_hooks) && fetch(:sneakers_monit_default_hooks)
invoke 'sneakers:monit:add_default_hooks'
end
end
end
namespace :sneakers do
namespace :monit do
desc 'Add Sneakers to monit'
task :add_default_hooks do
before 'deploy:updating', 'sneakers:monit:unmonitor'
after 'deploy:published', 'sneakers:monit:monitor'
end
desc 'Config Sneakers monit-service'
task :config do
on roles(fetch(:sneakers_role)) do |role|
on roles(fetch(:sneakers_roles)) do |role|
@role = role
template_sneakers 'sneakers_monit.conf', "#{fetch(:tmp_dir)}/monit.conf", @role
sudo_if_needed "mv #{fetch(:tmp_dir)}/monit.conf #{fetch(:sneakers_monit_conf_dir)}"
sudo_if_needed "#{fetch(:sneakers_monit_bin)} reload"
upload_sneakers_template 'sneakers_monit', "#{fetch(:tmp_dir)}/monit.conf", @role
mv_command = "mv #{fetch(:tmp_dir)}/monit.conf #{fetch(:sneakers_monit_conf_dir)}/#{sneakers_service_name}.conf"
sudo_if_needed mv_command
sudo_if_needed "#{fetch(:monit_bin)} reload"
end
end
desc 'Enable Sneakers monit'
desc 'Monitor Sneakers monit-service'
task :monitor do
on roles(fetch(:sneakers_role)) do
sudo_if_needed "#{fetch(:sneakers_monit_bin)} monitor #{sneakers_monit_service_name}"
on roles(fetch(:sneakers_roles)) do
sudo_if_needed "#{fetch(:monit_bin)} monitor #{sneakers_service_name}"
end
end
desc 'Disable Sneakers monit'
desc 'Unmonitor Sneakers monit-service'
task :unmonitor do
on roles(fetch(:sneakers_role)) do
sudo_if_needed "#{fetch(:sneakers_monit_bin)} unmonitor #{sneakers_monit_service_name}"
on roles(fetch(:sneakers_roles)) do
sudo_if_needed "#{fetch(:monit_bin)} unmonitor #{sneakers_service_name}"
end
end
desc 'Start Sneakers through monit'
desc 'Start Sneakers monit-service'
task :start do
on roles(fetch(:sneakers_role)) do
sudo_if_needed "#{fetch(:sneakers_monit_bin)} start #{sneakers_monit_service_name}"
on roles(fetch(:sneakers_roles)) do
sudo_if_needed "#{fetch(:monit_bin)} start #{sneakers_service_name}"
end
end
desc 'Stop Sneakers through monit'
desc 'Stop Sneakers monit-service'
task :stop do
on roles(fetch(:sneakers_role)) do
sudo_if_needed "#{fetch(:sneakers_monit_bin)} stop #{sneakers_monit_service_name}"
on roles(fetch(:sneakers_roles)) do
sudo_if_needed "#{fetch(:monit_bin)} stop #{sneakers_service_name}"
end
end
desc 'Restart Sneakers through monit'
desc 'Restart Sneakers monit-service'
task :restart do
on roles(fetch(:sneakers_role)) do
sudo_if_needed "#{fetch(:sneakers_monit_bin)} restart #{sneakers_monit_service_name}"
on roles(fetch(:sneakers_roles)) do
sudo_if_needed "#{fetch(:monit_bin)} restart #{sneakers_service_name}"
end
end
before 'deploy:updating', 'sneakers:monit:unmonitor'
after 'deploy:published', 'sneakers:monit:monitor'
def sneakers_monit_service_name
fetch(:sneakers_monit_service_name, "sneakers_#{fetch(:application)}_#{fetch(:stage)}")
def sneakers_service_name
fetch(:sneakers_service_name, "sneakers_#{fetch(:application)}_#{fetch(:sneakers_env)}")
end
def sudo_if_needed(command)
fetch(:sneakers_monit_use_sudo) ? sudo(command) : execute(command)
end
def template_sneakers(from, to, role)
[
File.join('lib', 'capistrano', 'templates', "#{from}-#{role.hostname}-#{fetch(:stage)}.rb"),
File.join('lib', 'capistrano', 'templates', "#{from}-#{role.hostname}.rb"),
File.join('lib', 'capistrano', 'templates', "#{from}-#{fetch(:stage)}.rb"),
File.join('lib', 'capistrano', 'templates', "#{from}.rb.erb"),
File.join('lib', 'capistrano', 'templates', "#{from}.rb"),
File.join('lib', 'capistrano', 'templates', "#{from}.erb"),
File.expand_path("../../templates/#{from}.rb.erb", __FILE__),
File.expand_path("../../templates/#{from}.erb", __FILE__)
].each do |path|
if File.file?(path)
erb = File.read(path)
upload! StringIO.new(ERB.new(erb).result(binding)), to
break
end
end
def upload_sneakers_template(from, to, role)
template = sneakers_template(from, role)
upload!(StringIO.new(ERB.new(template).result(binding)), to)
end
def sneakers_template(name, role)
local_template_directory = fetch(:sneakers_monit_templates_path)
search_paths = [
"#{name}-#{role.hostname}-#{fetch(:stage)}.erb",
"#{name}-#{role.hostname}.erb",
"#{name}-#{fetch(:stage)}.erb",
"#{name}.erb"
].map { |filename| File.join(local_template_directory, filename) }
global_search_path = File.expand_path(
File.join(*%w[.. .. .. generators capistrano sneakers monit templates], "#{name}.conf.erb"),
__FILE__
)
search_paths << global_search_path
template_path = search_paths.detect { |path| File.file?(path) }
File.read(template_path)
end
end
end
namespace :load do
task :defaults do
set :sneakers_default_hooks, -> { true }
set :sneakers_default_hooks, true
set :sneakers_pid, -> { File.join(shared_path, 'tmp', 'pids', 'sneakers.pid') }
set :sneakers_env, -> { fetch(:rack_env, fetch(:rails_env, fetch(:stage))) }
set :sneakers_log, -> { File.join(shared_path, 'log', 'sneakers.log') }
# set :sneakers_timeout, -> { 10 }
set :sneakers_role, -> { :app }
set :sneakers_processes, -> { 1 }
set :sneakers_workers, -> { false } # if this is false it will cause Capistrano to exit
set :sneakers_run_config, -> { false } # if this is true sneakers will run with preconfigured /config/initializers/sneakers.rb
set :sneakers_boot_file, -> { false } # Needed for booting daemons dynamically
# set :sneakers_timeout, -> 10
# TODO: Rename to plural
set :sneakers_roles, [:app]
set :sneakers_processes, 1
set :sneakers_workers, false # if this is false it will cause Capistrano to exit
# rename to sneakers_config
set :sneakers_run_config, true # if this is true sneakers will run with preconfigured /config/initializers/sneakers.rb
# Rbenv and RVM integration
set :rbenv_map_bins, fetch(:rbenv_map_bins).to_a.concat(%w(sneakers))
set :rvm_map_bins, fetch(:rvm_map_bins).to_a.concat(%w(sneakers))
......@@ -28,128 +29,6 @@ namespace :deploy do
end
namespace :sneakers do
def for_each_sneakers_process(reverse = false, &block)
pids = processes_sneakers_pids
pids.reverse! if reverse
pids.each_with_index do |pid_file, idx|
within current_path do
yield(pid_file, idx)
end
end
end
def processes_sneakers_pids
pids = []
raise "sneaker_processes is nil class, cannot continue, please [set :sneaker_processes]" if fetch(:sneakers_processes).nil?
fetch(:sneakers_processes).times do |idx|
pids.push (idx.zero? && fetch(:sneakers_processes) <= 1) ?
fetch(:sneakers_pid) :
fetch(:sneakers_pid).gsub(/\.pid$/, "-#{idx}.pid")
end
pids
end
def sneakers_pid_process_exists?(pid_file)
sneakers_pid_file_exists?(pid_file) and test(:kill, "-0 $( cat #{pid_file} )")
end
def sneakers_pid_file_exists?(pid_file)
test(*("[ -f #{pid_file} ]").split(' '))
end
def stop_sneakers(pid_file)
if fetch(:sneakers_run_config) == true
execute :kill, "-SIGTERM `cat #{pid_file}`"
else
if fetch(:stop_sneakers_in_background, fetch(:sneakers_run_in_background))
if fetch(:sneakers_use_signals)
background :kill, "-TERM `cat #{pid_file}`"
else
background :bundle, :exec, :sneakersctl, 'stop', "#{pid_file}", fetch(:sneakers_timeout)
end
else
execute :bundle, :exec, :sneakersctl, 'stop', "#{pid_file}", fetch(:sneakers_timeout)
end
end
end
def quiet_sneakers(pid_file)
if fetch(:sneakers_use_signals) || fetch(:sneakers_run_config)
execute :kill, "-USR1 `cat #{pid_file}`"
else
begin
execute :bundle, :exec, :sneakersctl, 'quiet', "#{pid_file}"
rescue SSHKit::Command::Failed
# If gems are not installed eq(first deploy) and sneakers_default_hooks as active
warn 'sneakersctl not found (ignore if this is the first deploy)'
end
end
end
def start_sneakers(pid_file, idx = 0)
if fetch(:sneakers_run_config) == true
# Use sneakers configuration prebuilt in
raise "[ set :workers, ['worker1', 'workerN'] ] not configured properly, please configure the workers you wish to use" if fetch(:sneakers_workers).nil? or fetch(:sneakers_workers) == false or !fetch(:sneakers_workers).kind_of? Array
workers = fetch(:sneakers_workers).compact.join(',')
#run "cmd", env: { 'WORKERS' => workers } #export this to environmental variable
info "Starting the sneakers processes"
#workers.each do |worker|
with rails_env: fetch(:sneakers_env), workers: workers do
rake 'sneakers:run'
end
#execute :bundle, :exec, :sneakers, args.compact.join(' ')
else
args = []
# Using custom sneakers setup
args.push "--index #{idx}"
args.push "--pidfile #{pid_file}"
args.push "--environment #{fetch(:sneakers_env)}"
args.push "--logfile #{fetch(:sneakers_log)}" if fetch(:sneakers_log)
args.push "--require #{fetch(:sneakers_require)}" if fetch(:sneakers_require)
args.push "--tag #{fetch(:sneakers_tag)}" if fetch(:sneakers_tag)
Array(fetch(:sneakers_queue)).each do |queue|
args.push "--queue #{queue}"
end
args.push "--config #{fetch(:sneakers_config)}" if fetch(:sneakers_config)
args.push "--concurrency #{fetch(:sneakers_concurrency)}" if fetch(:sneakers_concurrency)
# use sneakers_options for special options
args.push fetch(:sneakers_options) if fetch(:sneakers_options)
if defined?(JRUBY_VERSION)
args.push '>/dev/null 2>&1 &'
warn 'Since JRuby doesn\'t support Process.daemon, sneakers will not be running as a daemon.'
else
args.push '--daemon'
end
if fetch(:start_sneakers_in_background, fetch(:sneakers_run_in_background))
background :bundle, :exec, :sneakers, args.compact.join(' ')
else
execute :bundle, :exec, :sneakers, args.compact.join(' ')
end
end
end
def as_sneakers_user(role, &block)
user = sneakers_user(role)
if user == role.user
block.call
else
as user do
block.call
end
end
end
def sneakers_user(role)
properties = role.properties
properties.fetch(:sneakers_user) || fetch(:sneakers_user) || properties.fetch(:run_as) || role.user
end
task :add_default_hooks do
after 'deploy:starting', 'sneakers:quiet'
after 'deploy:updated', 'sneakers:stop'
......@@ -159,11 +38,11 @@ namespace :sneakers do
desc 'Quiet sneakers (stop processing new tasks)'
task :quiet do
on roles fetch(:sneakers_role) do |role|
as_sneakers_user(role) do
if test("[ -d #{current_path} ]") # fixes #11
for_each_sneakers_process(true) do |pid_file, idx|
if sneakers_pid_process_exists?(pid_file)
on roles fetch(:sneakers_roles) do |role|
switch_user(role) do
if test("[ -d #{current_path} ]")
each_process_with_index(true) do |pid_file, idx|
if pid_file_exists?(pid_file) && process_exists?(pid_file)
quiet_sneakers(pid_file)
end
end
......@@ -174,11 +53,11 @@ namespace :sneakers do
desc 'Stop sneakers'
task :stop do
on roles fetch(:sneakers_role) do |role|
as_sneakers_user(role) do
on roles fetch(:sneakers_roles) do |role|
switch_user(role) do
if test("[ -d #{current_path} ]")
for_each_sneakers_process(true) do |pid_file, idx|
if sneakers_pid_process_exists?(pid_file)
each_process_with_index(true) do |pid_file, idx|
if pid_file_exists?(pid_file) && process_exists?(pid_file)
stop_sneakers(pid_file)
end
end
......@@ -189,10 +68,10 @@ namespace :sneakers do
desc 'Start sneakers'
task :start do
on roles fetch(:sneakers_role) do |role|
as_sneakers_user(role) do
for_each_sneakers_process do |pid_file, idx|
unless sneakers_pid_process_exists?(pid_file)
on roles fetch(:sneakers_roles) do |role|
switch_user(role) do
each_process_with_index do |pid_file, idx|
unless pid_file_exists?(pid_file) && process_exists?(pid_file)
start_sneakers(pid_file, idx)
end
end
......@@ -211,10 +90,10 @@ namespace :sneakers do
desc 'Rolling-restart sneakers'
task :rolling_restart do
on roles fetch(:sneakers_role) do |role|
as_sneakers_user(role) do
for_each_sneakers_process(true) do |pid_file, idx|
if sneakers_pid_process_exists?(pid_file)
on roles fetch(:sneakers_roles) do |role|
switch_user(role) do
each_process_with_index(true) do |pid_file, idx|
if pid_file_exists?(pid_file) && process_exists?(pid_file)
stop_sneakers(pid_file)
end
start_sneakers(pid_file, idx)
......@@ -225,11 +104,13 @@ namespace :sneakers do
# Delete any pid file not in use
task :cleanup do
on roles fetch(:sneakers_role) do |role|
as_sneakers_user(role) do
for_each_sneakers_process do |pid_file, idx|
if sneakers_pid_file_exists?(pid_file)
execute "rm #{pid_file}" unless sneakers_pid_process_exists?(pid_file)
on roles fetch(:sneakers_roles) do |role|
switch_user(role) do
each_process_with_index do |pid_file, idx|
unless process_exists?(pid_file)
if pid_file_exists?(pid_file)
execute "rm #{pid_file}"
end
end
end
end
......@@ -240,14 +121,105 @@ namespace :sneakers do
desc 'Respawn missing sneakers proccesses'
task :respawn do
invoke 'sneakers:cleanup'
on roles fetch(:sneakers_role) do |role|
as_sneakers_user(role) do
for_each_sneakers_process do |pid_file, idx|
unless sneakers_pid_file_exists?(pid_file)
on roles fetch(:sneakers_roles) do |role|
switch_user(role) do
each_process_with_index do |pid_file, idx|
unless pid_file_exists?(pid_file)
start_sneakers(pid_file, idx)
end
end
end
end
end
def each_process_with_index(reverse = false, &block)
_pid_files = pid_files
_pid_files.reverse! if reverse
_pid_files.each_with_index do |pid_file, idx|
within release_path do
yield(pid_file, idx)
end
end
end
def pid_files
sneakers_roles = Array(fetch(:sneakers_roles))
sneakers_roles.select! { |role| host.roles.include?(role) }
sneakers_roles.flat_map do |role|
processes = fetch(:sneakers_processes)
if processes == 1
fetch(:sneakers_pid)
else
Array.new(processes) { |idx| fetch(:sneakers_pid).gsub(/\.pid$/, "-#{idx}.pid") }
end
end
end
def pid_file_exists?(pid_file)
test(*("[ -f #{pid_file} ]").split(' '))
end
def process_exists?(pid_file)
test(*("kill -0 $( cat #{pid_file} )").split(' '))
end
def quiet_sneakers(pid_file)
if fetch(:sneakers_use_signals) || fetch(:sneakers_run_config)
execute :kill, "-USR1 `cat #{pid_file}`"
else
begin
execute :bundle, :exec, :sneakersctl, 'quiet', "#{pid_file}"
rescue SSHKit::Command::Failed
# If gems are not installed eq(first deploy) and sneakers_default_hooks as active
warn 'sneakersctl not found (ignore if this is the first deploy)'
end
end
end
def stop_sneakers(pid_file)
if fetch(:sneakers_run_config) == true
execute :kill, "-SIGTERM `cat #{pid_file}`"
else
if fetch(:stop_sneakers_in_background, fetch(:sneakers_run_in_background))
if fetch(:sneakers_use_signals)
background :kill, "-TERM `cat #{pid_file}`"
else
background :bundle, :exec, :sneakersctl, 'stop', "#{pid_file}", fetch(:sneakers_timeout)
end
else
execute :bundle, :exec, :sneakersctl, 'stop', "#{pid_file}", fetch(:sneakers_timeout)
end
end
end
def start_sneakers(pid_file, idx = 0)
if fetch(:sneakers_run_config) == true
# Use sneakers configuration prebuilt in
raise "[ set :workers, ['worker1', 'workerN'] ] not configured properly, please configure the workers you wish to use" if fetch(:sneakers_workers).nil? or fetch(:sneakers_workers) == false or !fetch(:sneakers_workers).kind_of? Array
workers = fetch(:sneakers_workers).compact.join(',')
info "Starting the sneakers processes"
with rails_env: fetch(:sneakers_env), workers: workers do
rake 'sneakers:run'
end
end
end
def switch_user(role, &block)
user = sneakers_user(role)
if user == role.user
block.call
else
as user do
block.call
end
end
end
def sneakers_user(role)
properties = role.properties
properties.fetch(:sneakers_user) || fetch(:sneakers_user) || properties.fetch(:run_as) || role.user
end
end
require 'rails/generators/base'
module Capistrano
module Sneakers
module Monit
module Generators
class TemplateGenerator < Rails::Generators::Base
namespace "capistrano:sneakers:monit:template"
desc "Create local monitrc.erb, and erb files for monitored processes for customization"
source_root File.expand_path('../templates', __FILE__)
argument :templates_path, type: :string,
default: "config/deploy/templates",
banner: "path to templates"
def copy_template
copy_file "sneakers_monit.conf.erb", "#{templates_path}/sidekiq_monit.erb"
end
end
end
end
end
end
# Monit configuration for Sneakers
# Service name: <%= sneakers_monit_service_name %>
# Service name: <%= sneakers_service_name %>
#
check process <%= sneakers_monit_service_name %>
check process <%= sneakers_service_name %>
with pidfile "<%= fetch(:sneakers_pid) %>"
start program = "/usr/bin/sudo -iu <%= sneakers_user(@role) %> /bin/bash -c 'cd <%= current_path %> && RAILS_ENV=<%= fetch(:sneakers_env) %> WORKERS=<%= fetch(:sneakers_workers).join(',') %> <%= SSHKit.config.command_map[:rake] %> sneakers:run'"
stop program = "/usr/bin/sudo -iu <%= sneakers_user(@role) %> /bin/bash -c 'kill -SIGTERM `cat <%= fetch(:sneakers_pid) %>`'"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment