capsaicin 0.1.2
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.
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +43 -0
- data/VERSION.yml +4 -0
- data/lib/capsaicin.rb +10 -0
- data/lib/capsaicin/bundle.rb +7 -0
- data/lib/capsaicin/files.rb +47 -0
- data/lib/capsaicin/files/local.rb +49 -0
- data/lib/capsaicin/files/remote.rb +117 -0
- data/lib/capsaicin/invocation.rb +48 -0
- data/lib/capsaicin/service.rb +21 -0
- data/lib/capsaicin/service/command.rb +44 -0
- data/lib/capsaicin/service/crm.rb +84 -0
- data/lib/capsaicin/service/lsb.rb +48 -0
- data/lib/capsaicin/service/windows.rb +62 -0
- data/lib/capsaicin/sys.rb +11 -0
- data/lib/capsaicin/ui.rb +20 -0
- metadata +91 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Joe Khoobyar
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/testtask'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |s|
|
10
|
+
s.name = "capsaicin"
|
11
|
+
s.homepage = "http://github.com/joekhoobyar/capsaicin"
|
12
|
+
s.summary = "Joe Khoobyar's spicy capistrano extensions"
|
13
|
+
s.description = %Q{Spicy capistrano extensions for various needs}
|
14
|
+
|
15
|
+
s.authors = ["Joe Khoobyar"]
|
16
|
+
s.email = "joe@ankhcraft.com"
|
17
|
+
|
18
|
+
s.add_dependency 'capistrano', ['>= 2.0']
|
19
|
+
s.add_dependency 'archive-tar-minitar', ['>= 0.5']
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
23
|
+
end
|
24
|
+
|
25
|
+
# --------- RDoc Documentation ---------
|
26
|
+
desc "Generate RDoc documentation"
|
27
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
28
|
+
rdoc.options << '--line-numbers' << '--inline-source' <<
|
29
|
+
'--main' << 'README' <<
|
30
|
+
'--title' << "Capsaicin" <<
|
31
|
+
'--charset' << 'utf-8'
|
32
|
+
rdoc.rdoc_dir = "doc"
|
33
|
+
rdoc.rdoc_files.include 'README*'
|
34
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::TestTask.new do |t|
|
38
|
+
t.libs << "test"
|
39
|
+
t.libs << "lib"
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :build
|
43
|
+
|
data/VERSION.yml
ADDED
data/lib/capsaicin.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
unless Capistrano::Configuration.respond_to?(:instance)
|
2
|
+
abort "capsaicin requires capistrano 2.0 or later"
|
3
|
+
end
|
4
|
+
require 'capistrano'
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), %w(capsaicin sys))
|
7
|
+
require File.join(File.dirname(__FILE__), %w(capsaicin invocation))
|
8
|
+
require File.join(File.dirname(__FILE__), %w(capsaicin files))
|
9
|
+
require File.join(File.dirname(__FILE__), %w(capsaicin service))
|
10
|
+
require File.join(File.dirname(__FILE__), %w(capsaicin ui))
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
module Files
|
3
|
+
|
4
|
+
COMMANDS = [ %w(mkdir mkdir_p rmdir cp cp_r rm rm_f rm_r rm_rf
|
5
|
+
chmod chmod_R chown chown_R touch),
|
6
|
+
%w(ln ln_s ln_sf mv install) ]
|
7
|
+
|
8
|
+
FILE_TESTS = [
|
9
|
+
%w(blockdev? -b),
|
10
|
+
%w(chardev? -c),
|
11
|
+
%w(directory? -d),
|
12
|
+
%w(exists? -e),
|
13
|
+
%w(file? -f),
|
14
|
+
%w(grpowned? -G),
|
15
|
+
%w(owned? -O),
|
16
|
+
%w(pipe? -p),
|
17
|
+
%w(readable? -r),
|
18
|
+
%w(setgid? -g),
|
19
|
+
%w(setuid? -u),
|
20
|
+
%w(size? -s),
|
21
|
+
%w(socket? -S),
|
22
|
+
%w(sticky? -k),
|
23
|
+
%w(symlink? -h),
|
24
|
+
%w(writable? -w),
|
25
|
+
%w(executable? -x)
|
26
|
+
]
|
27
|
+
|
28
|
+
LOCAL_RUN_METHODS = [:system, :local_run]
|
29
|
+
|
30
|
+
require File.join(File.dirname(__FILE__), %w(files local.rb))
|
31
|
+
require File.join(File.dirname(__FILE__), %w(files remote.rb))
|
32
|
+
|
33
|
+
class_eval(Local.public_instance_methods(false).map do |m|
|
34
|
+
"def #{m}(*args, &block)\n send(_via.to_s + '_files').#{m}(*args, &block)\nend"
|
35
|
+
end.join("\n"), __FILE__, __LINE__)
|
36
|
+
|
37
|
+
def _via
|
38
|
+
if LOCAL_RUN_METHODS.include? @config.fetch(:run_method, nil)
|
39
|
+
:local
|
40
|
+
else
|
41
|
+
:remote
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Capistrano.plugin :files, Capsaicin::Files
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Capsaicin
|
4
|
+
module Files
|
5
|
+
module Local
|
6
|
+
|
7
|
+
include FileUtils::Verbose
|
8
|
+
|
9
|
+
public *COMMANDS.flatten
|
10
|
+
public :pwd
|
11
|
+
|
12
|
+
FILE_TESTS.each do |m,t|
|
13
|
+
class_eval <<-EODEF, __FILE__, __LINE__
|
14
|
+
def #{m}(a, options={})
|
15
|
+
logger.trace "test #{t} \#{a.gsub ' ', '\\ '}" if logger
|
16
|
+
File.#{m} a
|
17
|
+
end
|
18
|
+
EODEF
|
19
|
+
end
|
20
|
+
|
21
|
+
def tail_f(file, n=10)
|
22
|
+
unless defined? File::Tail::Logfile then gem 'file-tail'; require 'file/tail' end
|
23
|
+
File::Tail::Logfile.tail(file, :backward=>n) do |line| puts line end
|
24
|
+
rescue Interrupt
|
25
|
+
logger.trace "interrupted (Ctrl-C)" if logger
|
26
|
+
end
|
27
|
+
|
28
|
+
def upload(from, to)
|
29
|
+
cp from, to
|
30
|
+
end
|
31
|
+
|
32
|
+
def download(from, to)
|
33
|
+
cp from, to
|
34
|
+
end
|
35
|
+
|
36
|
+
def cd(dir, options={})
|
37
|
+
if block_given?
|
38
|
+
dir, dir2 = pwd, dir
|
39
|
+
cd dir2
|
40
|
+
yield
|
41
|
+
end
|
42
|
+
cd dir
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Capistrano.plugin :local_files, Capsaicin::Files::Local
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Capsaicin
|
4
|
+
module Files
|
5
|
+
module Remote
|
6
|
+
|
7
|
+
COMMANDS.each_with_index do |l,n|
|
8
|
+
l.each do |m|
|
9
|
+
k, f = m.split('_')
|
10
|
+
f = ' -' + f if f
|
11
|
+
class_eval <<-EODEF, __FILE__, __LINE__
|
12
|
+
def #{m}(*args)
|
13
|
+
options = args.pop if Hash === args.last
|
14
|
+
_r '#{k}#{f}', args#{', ' + (n+1).to_s if n > 0}
|
15
|
+
end
|
16
|
+
EODEF
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
FILE_TESTS.each do |m,t|
|
21
|
+
class_eval <<-EODEF
|
22
|
+
def #{m}(a, options={})
|
23
|
+
_t "test #{t}", a
|
24
|
+
end
|
25
|
+
EODEF
|
26
|
+
end
|
27
|
+
|
28
|
+
def tail_f(file, n=10)
|
29
|
+
cmd = "tail -n #{n} -f #{_q file}"
|
30
|
+
case v = _via
|
31
|
+
when :system, :local_run
|
32
|
+
send v, cmd
|
33
|
+
else
|
34
|
+
stream cmd, :via => v
|
35
|
+
end
|
36
|
+
rescue Interrupt
|
37
|
+
logger.trace "interrupted (Ctrl-C)" if logger
|
38
|
+
end
|
39
|
+
|
40
|
+
def upload(*args)
|
41
|
+
case _via
|
42
|
+
when :system, :local_run
|
43
|
+
cp(*args)
|
44
|
+
else
|
45
|
+
@config.upload(*args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def download(*args)
|
50
|
+
case _via
|
51
|
+
when :system, :local_run
|
52
|
+
cp(*args)
|
53
|
+
else
|
54
|
+
@config.download(*args)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def cd(dir, options={})
|
59
|
+
if block_given?
|
60
|
+
dir, dir2 = pwd, dir
|
61
|
+
_r 'cd', dir2
|
62
|
+
yield
|
63
|
+
end
|
64
|
+
_r 'cd', dir
|
65
|
+
end
|
66
|
+
|
67
|
+
def pwd
|
68
|
+
capture 'pwd', :via => _via
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def _t(cmd, args=nil, min=nil)
|
74
|
+
cmd = _a cmd, args, min
|
75
|
+
if _via == :system then
|
76
|
+
local_run(cmd)
|
77
|
+
else
|
78
|
+
capture("#{cmd}; echo $?", :via => _via).strip == '0'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def _r(cmd, args=nil, min=nil)
|
83
|
+
cmd = _a cmd, args, min
|
84
|
+
if _via != :system then
|
85
|
+
invoke_command cmd, :via => _via
|
86
|
+
else
|
87
|
+
local_run cmd
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def _a(cmd, args=nil, min=nil)
|
92
|
+
case args
|
93
|
+
when NilClass
|
94
|
+
raise ArgumentError unless min.nil? or min.zero?
|
95
|
+
when Array
|
96
|
+
args = args.flatten
|
97
|
+
raise ArgumentError if (min || 1) > args.length
|
98
|
+
cmd = "#{cmd} #{_q(*args)}" if args.any?
|
99
|
+
else
|
100
|
+
raise ArgumentError if min and min < 1
|
101
|
+
cmd = "#{cmd} #{_q args}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def _q(*list)
|
106
|
+
list.map { |l| "'#{l.gsub("'", "\\'")}'" }.join ' '
|
107
|
+
end
|
108
|
+
|
109
|
+
def _via
|
110
|
+
@config.fetch(:run_method, nil)
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
Capistrano.plugin :remote_files, Capsaicin::Files::Remote
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
|
3
|
+
module Invocation
|
4
|
+
|
5
|
+
# Capistrano's system() override is only available from the base deployment strategy.
|
6
|
+
# Also, we could do with a few more windows checks.
|
7
|
+
def local_run(*args, &block)
|
8
|
+
args.pop if Hash===args.last
|
9
|
+
cmd = args.join(' ')
|
10
|
+
if RUBY_PLATFORM =~ /win32|mingw|mswin/
|
11
|
+
cmd.gsub!('/','\\') # Replace / with \\
|
12
|
+
cmd.gsub!(/^cd /,'cd /D ') # Replace cd with cd /D
|
13
|
+
cmd.gsub!(/&& cd /,'&& cd /D ') # Replace cd with cd /D
|
14
|
+
logger.trace "executing locally: #{cmd}"
|
15
|
+
Kernel.system cmd
|
16
|
+
else
|
17
|
+
logger.trace "executing locally: #{cmd}"
|
18
|
+
Kernel.system cmd
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Always uses :runner to sudo as someone.
|
23
|
+
# Equivalent to: sudo "command", :as => fetch(:runner,nil)
|
24
|
+
def sudo_as(*args, &block)
|
25
|
+
options = Hash===args.last ? args.pop.dup : {}
|
26
|
+
options[:as] = fetch(:runner, nil)
|
27
|
+
sudo *args.push(options), &block
|
28
|
+
end
|
29
|
+
|
30
|
+
# Extremely helpful if you only have permission to: sudo su SOMEUSER -c "command"
|
31
|
+
def sudo_su(*args, &block)
|
32
|
+
options = Hash===args.last ? args.pop.dup : {}
|
33
|
+
args[0] = "su #{fetch(:runner, nil)} -c '#{args[0]}'"
|
34
|
+
sudo *args.push(options), &block
|
35
|
+
end
|
36
|
+
|
37
|
+
# Extremely helpful if you only have permission to: sudo su - SOMEUSER
|
38
|
+
def sudo_su_to(*args, &block)
|
39
|
+
options = Hash===args.last ? args.pop.dup : {}
|
40
|
+
options[:shell] = false
|
41
|
+
cmd = args[0].gsub(/[$\\`"]/) { |m| "\\#{m}" }
|
42
|
+
args[0] = "echo \"#{cmd}\" | #{sudo} su - #{fetch(:runner, nil)}"
|
43
|
+
run *args.push(options), &block
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Capistrano::Configuration.send :include, Invocation
|
48
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
|
3
|
+
module Service
|
4
|
+
|
5
|
+
SVC_ACTION_CAPTIONS = Hash.new do |h,k|
|
6
|
+
h[k] = "#{k.to_s.capitalize} Service"
|
7
|
+
end.update :status => 'Check Status',
|
8
|
+
:check => 'Check Config',
|
9
|
+
:summary => 'Status Summary'
|
10
|
+
|
11
|
+
%w(lsb crm windows command).each do |k|
|
12
|
+
require File.join(File.dirname(__FILE__), 'service', k+'.rb')
|
13
|
+
end
|
14
|
+
|
15
|
+
include LSB, CRM, Windows, Command
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
Capistrano.plugin :service, Capsaicin::Service
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
module Service
|
3
|
+
module Command
|
4
|
+
|
5
|
+
# Check for the existance of a generic Windows NT service.
|
6
|
+
def command?(start, stop)
|
7
|
+
files.executable? start and files.executable? stop
|
8
|
+
end
|
9
|
+
|
10
|
+
# Defines a recipe to control a generic Windows NT service.
|
11
|
+
#
|
12
|
+
def command(id,start,stop,*args)
|
13
|
+
options = Hash===args.last ? args.pop : {}
|
14
|
+
|
15
|
+
svc_name = id.to_s
|
16
|
+
svc_desc = next_description(:reset) || (svc_name.capitalize unless options.delete(:hide))
|
17
|
+
extras = args.pop if Array === args.last
|
18
|
+
via = options.delete(:via)
|
19
|
+
|
20
|
+
namespace id do
|
21
|
+
|
22
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:start]}" if svc_desc
|
23
|
+
task :start, options do
|
24
|
+
send(via || fetch(:run_method, :local_run), start)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:stop]}" if svc_desc
|
28
|
+
task :stop, options do
|
29
|
+
send(via || fetch(:run_method, :local_run), stop)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:restart]}" if svc_desc
|
33
|
+
task :restart, options do
|
34
|
+
stop
|
35
|
+
start
|
36
|
+
end
|
37
|
+
|
38
|
+
instance_eval { yield } if block_given?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
module Service
|
3
|
+
module CRM
|
4
|
+
|
5
|
+
DEFAULT_ACTIONS = [ [ :status, '-W' ],
|
6
|
+
[ :summary, "-x | awk '/^raw xml:/ { exit }; { print }'" ],
|
7
|
+
[ :start, "--meta -d 'target_role'" ],
|
8
|
+
[ :stop, "--meta -p 'target_role' -v 'stopped'" ] ]
|
9
|
+
|
10
|
+
OCF_DEFAULT_ACTIONS = [ [ :validate, '-c -C' ],
|
11
|
+
[ :monitor, '-c -m' ] ]
|
12
|
+
|
13
|
+
# Defines a recipe to control a cluster-managed service, using heartbeat or pacemaker.
|
14
|
+
#
|
15
|
+
def crm(id,*args)
|
16
|
+
options = Hash===args.last ? args.pop : {}
|
17
|
+
|
18
|
+
svc_name = id.to_s
|
19
|
+
svc_desc = next_description(:reset) || (svc_name.capitalize unless options.delete(:hide))
|
20
|
+
svc_actions = DEFAULT_ACTIONS
|
21
|
+
svc_actions += args.pop if Array === args.last
|
22
|
+
|
23
|
+
namespace id do
|
24
|
+
|
25
|
+
case args.first
|
26
|
+
when String; id = args.shift.intern
|
27
|
+
when Symbol; id = args.shift
|
28
|
+
end
|
29
|
+
svc_cmd = "/usr/sbin/crm_resource -r #{id.to_s.split(':').last}"
|
30
|
+
|
31
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:status]}" if svc_desc
|
32
|
+
task :default, options do
|
33
|
+
sudo "#{svc_cmd} -W"
|
34
|
+
end
|
35
|
+
|
36
|
+
svc_actions.each do |svc_action,svc_args|
|
37
|
+
svc_action = svc_action.intern unless Symbol===svc_action
|
38
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[svc_action]}" if svc_desc
|
39
|
+
task svc_action, options do
|
40
|
+
sudo "#{svc_cmd} #{svc_args}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
instance_eval { yield } if block_given?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Defines a recipe providing additional controls for a cluster-managed service,
|
49
|
+
# using the ocf_resource tool which interoperates with heartbeat or pacemaker.
|
50
|
+
#
|
51
|
+
# For more information about ocf_resource and other add-ons for heartbeat/pacemaker,
|
52
|
+
# see http://github.com/joekhoobyar/ha-tools
|
53
|
+
#
|
54
|
+
def crm_ocf(id,*args)
|
55
|
+
options = Hash===args.last ? args.pop : {}
|
56
|
+
|
57
|
+
svc_name = id.to_s
|
58
|
+
svc_desc = next_description(:reset) || (svc_name.capitalize unless options.delete(:hide))
|
59
|
+
svc_actions = DEFAULT_ACTIONS
|
60
|
+
svc_actions += args.pop if Array === args.last
|
61
|
+
|
62
|
+
namespace id do
|
63
|
+
|
64
|
+
case args.first
|
65
|
+
when String; id = args.shift.intern
|
66
|
+
when Symbol; id = args.shift
|
67
|
+
end
|
68
|
+
svc_cmd = "ocf_resource -g #{id.to_s.split(':').last}"
|
69
|
+
|
70
|
+
svc_actions.each do |svc_action,svc_args|
|
71
|
+
svc_action = svc_action.intern if String === svc_action
|
72
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[svc_action]}" if svc_desc
|
73
|
+
task svc_action, options do
|
74
|
+
sudo "#{svc_cmd} #{svc_args}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
instance_eval { yield } if block_given?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
module Service
|
3
|
+
module LSB
|
4
|
+
|
5
|
+
DEFAULT_ACTIONS = %w(status start stop restart)
|
6
|
+
|
7
|
+
# Check for the existance of a generic LSB initscript.
|
8
|
+
def lsb?(id, basedir="/etc/init.d")
|
9
|
+
files.exists? "#{basedir}/#{id.to_s.split(':').last}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Defines a recipe to control a generic LSB service.
|
13
|
+
#
|
14
|
+
def lsb(id,*args)
|
15
|
+
options = Hash===args.last ? args.pop : {}
|
16
|
+
|
17
|
+
basedir = options.delete(:basedir)
|
18
|
+
svc_name = id.to_s
|
19
|
+
svc_desc = next_description(:reset) || (svc_name.capitalize unless options.delete(:hide))
|
20
|
+
svc_actions = DEFAULT_ACTIONS
|
21
|
+
svc_actions += args.pop if Array === args.last
|
22
|
+
|
23
|
+
namespace id do
|
24
|
+
case args.first
|
25
|
+
when String; id = args.shift.intern
|
26
|
+
when Symbol; id = args.shift
|
27
|
+
end
|
28
|
+
svc_cmd = "#{basedir || '/etc/init.d'}/#{id.to_s.split(':').last}"
|
29
|
+
|
30
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:status]}" if svc_desc
|
31
|
+
task :default, options do
|
32
|
+
send(basedir ? run_method : :sudo, "#{svc_cmd} status")
|
33
|
+
end
|
34
|
+
|
35
|
+
svc_actions.each do |svc_action|
|
36
|
+
svc_action = svc_action.intern if String === svc_action
|
37
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[svc_action]}" if svc_desc
|
38
|
+
task svc_action, options do
|
39
|
+
send(basedir ? run_method : :sudo, "#{svc_cmd} #{svc_action}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
instance_eval { yield } if block_given?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
module Service
|
3
|
+
module Windows
|
4
|
+
|
5
|
+
DEFAULT_ACTIONS = [:start, :stop]
|
6
|
+
|
7
|
+
# Check for the existance of a generic Windows NT service.
|
8
|
+
def windows?(id, verbose=false)
|
9
|
+
logger.trace "executing locally: sc query \"#{id}\"" if logger and verbose
|
10
|
+
$1.to_i if `sc query "#{id}"` =~ /STATE +: +([0-9])+ +([^ ]+)/
|
11
|
+
ensure
|
12
|
+
logger.trace " service status => #{$2} (#{$1})" if logger and verbose
|
13
|
+
end
|
14
|
+
|
15
|
+
# Defines a recipe to control a generic Windows NT service.
|
16
|
+
#
|
17
|
+
def windows(id,*args)
|
18
|
+
options = Hash===args.last ? args.pop : {}
|
19
|
+
|
20
|
+
svc_name = id.to_s
|
21
|
+
svc_desc = next_description(:reset) || (svc_name.capitalize unless options.delete(:hide))
|
22
|
+
svc_actions = DEFAULT_ACTIONS
|
23
|
+
svc_actions += args.pop if Array === args.last
|
24
|
+
|
25
|
+
namespace id do
|
26
|
+
case args.first
|
27
|
+
when String; id = args.shift.intern
|
28
|
+
when Symbol; id = args.shift
|
29
|
+
end
|
30
|
+
|
31
|
+
[:default, :status].each do |k|
|
32
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:status]}" if svc_desc
|
33
|
+
task k, options do
|
34
|
+
service.windows? id, true or
|
35
|
+
abort "Failed to get service status for #{svc_name}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
DEFAULT_ACTIONS.each do |svc_action|
|
40
|
+
svc_action = svc_action.intern if String === svc_action
|
41
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[svc_action]}" if svc_desc
|
42
|
+
task svc_action, options do
|
43
|
+
local_run "net #{svc_action} \"#{id}\""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "#{svc_desc}: #{SVC_ACTION_CAPTIONS[:restart]}" if svc_desc
|
48
|
+
task :restart, options do
|
49
|
+
case service.windows?(id)
|
50
|
+
when 4, 2; stop
|
51
|
+
when NilClass; abort "Failed to get service status for #{svc_name}"
|
52
|
+
end
|
53
|
+
start
|
54
|
+
end
|
55
|
+
|
56
|
+
instance_eval { yield } if block_given?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
data/lib/capsaicin/ui.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Capsaicin
|
2
|
+
module UI
|
3
|
+
module AskPass
|
4
|
+
def password_prompt(prompt='Password: ')
|
5
|
+
if cap_askpass = ENV['CAP_ASKPASS']
|
6
|
+
`#{cap_askpass} "#{prompt}"`.strip
|
7
|
+
else
|
8
|
+
password_prompt_console
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
(class << Capistrano::CLI; self; end).class_eval do
|
14
|
+
alias :password_prompt_console :password_prompt
|
15
|
+
include AskPass
|
16
|
+
alias :password_prompt_askpass :password_prompt
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: capsaicin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joe Khoobyar
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-07 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: capistrano
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "2.0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: archive-tar-minitar
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0.5"
|
34
|
+
version:
|
35
|
+
description: Spicy capistrano extensions for various needs
|
36
|
+
email: joe@ankhcraft.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.rdoc
|
44
|
+
files:
|
45
|
+
- LICENSE
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- VERSION.yml
|
49
|
+
- lib/capsaicin.rb
|
50
|
+
- lib/capsaicin/bundle.rb
|
51
|
+
- lib/capsaicin/files.rb
|
52
|
+
- lib/capsaicin/files/local.rb
|
53
|
+
- lib/capsaicin/files/remote.rb
|
54
|
+
- lib/capsaicin/invocation.rb
|
55
|
+
- lib/capsaicin/service.rb
|
56
|
+
- lib/capsaicin/service/command.rb
|
57
|
+
- lib/capsaicin/service/crm.rb
|
58
|
+
- lib/capsaicin/service/lsb.rb
|
59
|
+
- lib/capsaicin/service/windows.rb
|
60
|
+
- lib/capsaicin/sys.rb
|
61
|
+
- lib/capsaicin/ui.rb
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/joekhoobyar/capsaicin
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --charset=UTF-8
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: "0"
|
82
|
+
version:
|
83
|
+
requirements: []
|
84
|
+
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 1.3.2
|
87
|
+
signing_key:
|
88
|
+
specification_version: 3
|
89
|
+
summary: Joe Khoobyar's spicy capistrano extensions
|
90
|
+
test_files: []
|
91
|
+
|