sunshine 1.0.0.pre
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/History.txt +237 -0
- data/Manifest.txt +70 -0
- data/README.txt +277 -0
- data/Rakefile +46 -0
- data/bin/sunshine +5 -0
- data/examples/deploy.rb +61 -0
- data/examples/deploy_tasks.rake +112 -0
- data/examples/standalone_deploy.rb +31 -0
- data/lib/commands/add.rb +96 -0
- data/lib/commands/default.rb +169 -0
- data/lib/commands/list.rb +322 -0
- data/lib/commands/restart.rb +62 -0
- data/lib/commands/rm.rb +83 -0
- data/lib/commands/run.rb +151 -0
- data/lib/commands/start.rb +72 -0
- data/lib/commands/stop.rb +61 -0
- data/lib/sunshine/app.rb +876 -0
- data/lib/sunshine/binder.rb +70 -0
- data/lib/sunshine/crontab.rb +143 -0
- data/lib/sunshine/daemon.rb +380 -0
- data/lib/sunshine/daemons/ar_sendmail.rb +28 -0
- data/lib/sunshine/daemons/delayed_job.rb +30 -0
- data/lib/sunshine/daemons/nginx.rb +104 -0
- data/lib/sunshine/daemons/rainbows.rb +35 -0
- data/lib/sunshine/daemons/server.rb +66 -0
- data/lib/sunshine/daemons/unicorn.rb +26 -0
- data/lib/sunshine/dependencies.rb +103 -0
- data/lib/sunshine/dependency_lib.rb +200 -0
- data/lib/sunshine/exceptions.rb +54 -0
- data/lib/sunshine/healthcheck.rb +83 -0
- data/lib/sunshine/output.rb +131 -0
- data/lib/sunshine/package_managers/apt.rb +48 -0
- data/lib/sunshine/package_managers/dependency.rb +349 -0
- data/lib/sunshine/package_managers/gem.rb +54 -0
- data/lib/sunshine/package_managers/yum.rb +62 -0
- data/lib/sunshine/remote_shell.rb +241 -0
- data/lib/sunshine/repo.rb +128 -0
- data/lib/sunshine/repos/git_repo.rb +122 -0
- data/lib/sunshine/repos/rsync_repo.rb +29 -0
- data/lib/sunshine/repos/svn_repo.rb +78 -0
- data/lib/sunshine/server_app.rb +554 -0
- data/lib/sunshine/shell.rb +384 -0
- data/lib/sunshine.rb +391 -0
- data/templates/logrotate/logrotate.conf.erb +11 -0
- data/templates/nginx/nginx.conf.erb +109 -0
- data/templates/nginx/nginx_optimize.conf +23 -0
- data/templates/nginx/nginx_proxy.conf +13 -0
- data/templates/rainbows/rainbows.conf.erb +18 -0
- data/templates/tasks/sunshine.rake +114 -0
- data/templates/unicorn/unicorn.conf.erb +6 -0
- data/test/fixtures/app_configs/test_app.yml +11 -0
- data/test/fixtures/sunshine_test/test_upload +0 -0
- data/test/mocks/mock_object.rb +179 -0
- data/test/mocks/mock_open4.rb +117 -0
- data/test/test_helper.rb +188 -0
- data/test/unit/test_app.rb +489 -0
- data/test/unit/test_binder.rb +20 -0
- data/test/unit/test_crontab.rb +128 -0
- data/test/unit/test_git_repo.rb +26 -0
- data/test/unit/test_healthcheck.rb +70 -0
- data/test/unit/test_nginx.rb +107 -0
- data/test/unit/test_rainbows.rb +26 -0
- data/test/unit/test_remote_shell.rb +102 -0
- data/test/unit/test_repo.rb +42 -0
- data/test/unit/test_server.rb +324 -0
- data/test/unit/test_server_app.rb +425 -0
- data/test/unit/test_shell.rb +97 -0
- data/test/unit/test_sunshine.rb +157 -0
- data/test/unit/test_svn_repo.rb +55 -0
- data/test/unit/test_unicorn.rb +22 -0
- metadata +217 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Simple server wrapper for nginx setup and control.
|
5
|
+
|
6
|
+
class Nginx < Server
|
7
|
+
|
8
|
+
def initialize app, options={}
|
9
|
+
super
|
10
|
+
|
11
|
+
@sudo ||= @port < 1024
|
12
|
+
|
13
|
+
@dep_name = use_passenger? ? 'passenger-nginx' : 'nginx'
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def start_cmd
|
18
|
+
"#{@bin} -c #{self.config_file_path}"
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
def stop_cmd
|
23
|
+
cmd = "test -f #{@pid} && kill -QUIT $(cat #{@pid})"+
|
24
|
+
" || echo 'No #{@name} process to stop for #{@app.name}';"
|
25
|
+
cmd << "sleep 2 ; rm -f #{@pid};"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def setup
|
30
|
+
super do |server_app, binder|
|
31
|
+
|
32
|
+
binder.forward :use_passenger?
|
33
|
+
|
34
|
+
binder.set :passenger_root do
|
35
|
+
passenger_root server_app.shell
|
36
|
+
end
|
37
|
+
|
38
|
+
binder.set :nginx_conf_path do
|
39
|
+
nginx_bin = server_app.shell.call "which nginx"
|
40
|
+
File.join File.dirname(nginx_bin), '..', 'conf'
|
41
|
+
end
|
42
|
+
|
43
|
+
yield(server_app, binder) if block_given?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
##
|
49
|
+
# Check if passenger is required to run the application.
|
50
|
+
# Returns true if the server's target is a Sunshine::App
|
51
|
+
|
52
|
+
def use_passenger?
|
53
|
+
Sunshine::App === @target
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
##
|
58
|
+
# Gets the root of the installer passenger gem.
|
59
|
+
|
60
|
+
def passenger_root shell
|
61
|
+
str = shell.call "gem list passenger -d"
|
62
|
+
version = $1 if str =~ /passenger\s\((.*)\)$/
|
63
|
+
gempath = $1 if str =~ /Installed\sat:\s(.*)$/
|
64
|
+
|
65
|
+
return unless version && gempath
|
66
|
+
|
67
|
+
File.join(gempath, "gems/passenger-#{version}")
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
##
|
72
|
+
# Run passenger installation for nginx
|
73
|
+
|
74
|
+
def setup_passenger server_app
|
75
|
+
server_app.install_deps 'passenger'
|
76
|
+
|
77
|
+
server_app.shell.call \
|
78
|
+
'passenger-install-nginx-module --auto --auto-download',
|
79
|
+
:sudo => true do |stream, data, inn|
|
80
|
+
|
81
|
+
if data =~ /Please specify a prefix directory \[(.*)\]:/
|
82
|
+
|
83
|
+
dir = $1
|
84
|
+
inn.puts dir
|
85
|
+
|
86
|
+
required_dirs = [
|
87
|
+
File.join(dir, 'fastcgi_temp'),
|
88
|
+
File.join(dir, 'proxy_temp')
|
89
|
+
]
|
90
|
+
|
91
|
+
server_app.shell.call \
|
92
|
+
"mkdir -p #{required_dirs.join(" ")}", :sudo => true
|
93
|
+
|
94
|
+
error_log = File.join(dir, "logs/error.log")
|
95
|
+
|
96
|
+
server_app.shell.call \
|
97
|
+
"touch #{error_log} && chmod a+rw #{error_log}", :sudo => true
|
98
|
+
|
99
|
+
server_app.add_shell_paths File.join(dir, 'sbin')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Simple server wrapper for Rainbows setup and control.
|
5
|
+
|
6
|
+
class Rainbows < Unicorn
|
7
|
+
|
8
|
+
attr_reader :concurrency
|
9
|
+
|
10
|
+
##
|
11
|
+
# Assign and/or use a concurrency model. Supports all Rainbows concurrency
|
12
|
+
# models; defaults to :ThreadSpawn
|
13
|
+
# Allows options:
|
14
|
+
# :model:: :ConcurrModel - concurrency model. Defaults to ThreadSpawn
|
15
|
+
# :connections:: int - the number of worker connections to use.
|
16
|
+
# :timeout:: seconds - the keepalive timeout. zero disables keepalives.
|
17
|
+
|
18
|
+
def use_concurrency options=nil
|
19
|
+
@concurrency ||= {:model => :ThreadSpawn}
|
20
|
+
@concurrency.merge! options
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
##
|
25
|
+
# Setup Rainbows specific bindings before building its config.
|
26
|
+
|
27
|
+
def setup
|
28
|
+
super do |server_app, binder|
|
29
|
+
binder.forward :concurrency
|
30
|
+
|
31
|
+
yield(server_app, binder) if block_given?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# An abstract class to wrap simple server software setup and start/stop.
|
5
|
+
#
|
6
|
+
# Child classes are expected to at least provide a start and stop bash script
|
7
|
+
# by either overloading the start_cmd and stop_cmd methods, or by setting
|
8
|
+
# @start_cmd and @stop_cmd. A restart_cmd method or @restart_cmd attribute
|
9
|
+
# may also be specified if restart requires more functionality than simply
|
10
|
+
# calling start_cmd && stop_cmd.
|
11
|
+
|
12
|
+
class Server < Daemon
|
13
|
+
|
14
|
+
def self.binder_methods
|
15
|
+
[:server_name, :port].concat super
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
attr_reader :server_name, :port
|
20
|
+
|
21
|
+
|
22
|
+
# Server objects need only an App object to be instantiated.
|
23
|
+
# All Daemon init options are supported plus the following:
|
24
|
+
#
|
25
|
+
# :port:: port_num - the port to run the server on
|
26
|
+
# defaults to 80
|
27
|
+
#
|
28
|
+
# :server_name:: myserver.com - host name used by server
|
29
|
+
# defaults to nil
|
30
|
+
#
|
31
|
+
# By default, servers also assign the option :role => :web.
|
32
|
+
|
33
|
+
def initialize app, options={}
|
34
|
+
options[:role] ||= :web
|
35
|
+
|
36
|
+
super app, options
|
37
|
+
|
38
|
+
@port = options[:port] || 80
|
39
|
+
@server_name = options[:server_name]
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def config_binding shell
|
46
|
+
binder = super
|
47
|
+
|
48
|
+
binder.set :server_name, (@server_name || shell.host)
|
49
|
+
|
50
|
+
binder
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def register_after_user_script
|
55
|
+
super
|
56
|
+
|
57
|
+
@app.after_user_script do |app|
|
58
|
+
next unless @port
|
59
|
+
|
60
|
+
each_server_app do |sa|
|
61
|
+
sa.info[:ports][@pid] = @port
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Simple server wrapper for Unicorn setup and control.
|
5
|
+
|
6
|
+
class Unicorn < Server
|
7
|
+
|
8
|
+
def initialize app, options={}
|
9
|
+
super
|
10
|
+
@timeout = options[:timeout] || 3.0
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def start_cmd
|
15
|
+
"cd #{@app.current_path} && #{@bin} -D -E"+
|
16
|
+
" #{@app.deploy_env} -p #{@port} -c #{self.config_file_path};"
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def stop_cmd
|
21
|
+
"test -f #{@pid} && kill -QUIT $(cat #{@pid})"+
|
22
|
+
" || echo 'No #{@name} process to stop for #{@app.name}';"+
|
23
|
+
"sleep 2; rm -f #{@pid};"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
##
|
2
|
+
# Defines Sunshine deploy server dependencies.
|
3
|
+
|
4
|
+
#class Sunshine::Dependencies < Settler
|
5
|
+
Sunshine.dependencies.instance_eval do
|
6
|
+
|
7
|
+
yum 'tpkg'
|
8
|
+
|
9
|
+
apt 'svn', :pkg => 'subversion'
|
10
|
+
yum 'svn', :pkg => 'subversion'
|
11
|
+
|
12
|
+
apt 'git', :pkg => 'git-core'
|
13
|
+
yum 'git', :pkg => 'git-core'
|
14
|
+
|
15
|
+
apt 'nginx'
|
16
|
+
yum 'nginx'
|
17
|
+
|
18
|
+
apt 'logrotate'
|
19
|
+
yum 'logrotate'
|
20
|
+
|
21
|
+
apt 'ruby', :pkg => 'ruby-full'
|
22
|
+
yum 'ruby'
|
23
|
+
|
24
|
+
apt 'ruby-devel', :pkg => 'ruby-dev'
|
25
|
+
yum 'ruby-devel'
|
26
|
+
|
27
|
+
apt 'irb'
|
28
|
+
yum 'irb', :pkg => 'ruby-irb'
|
29
|
+
|
30
|
+
apt 'rubygems', :version => '1.3.5' do
|
31
|
+
requires 'ruby', 'ruby-devel'
|
32
|
+
end
|
33
|
+
yum 'rubygems', :version => '1.3.5' do
|
34
|
+
requires 'ruby', 'ruby-devel'
|
35
|
+
end
|
36
|
+
|
37
|
+
apt 'logrotate'
|
38
|
+
yum 'logrotate'
|
39
|
+
|
40
|
+
apt 'curl-devel', :pkg => 'libcurl-dev'
|
41
|
+
yum 'curl-devel'
|
42
|
+
|
43
|
+
apt 'libxml2-devel', :pkg => 'libxml2-dev'
|
44
|
+
yum 'libxml2-devel'
|
45
|
+
|
46
|
+
apt 'libxslt-devel', :pkg => 'libxslt-dev'
|
47
|
+
yum 'libxslt-devel'
|
48
|
+
|
49
|
+
apt 'sqlite', :pkg => 'sqlite3'
|
50
|
+
yum 'sqlite'
|
51
|
+
|
52
|
+
apt 'sqlite-devel', :pkg => 'libsqlite3-dev'
|
53
|
+
yum 'sqlite-devel'
|
54
|
+
|
55
|
+
|
56
|
+
# Define gems used by Sunshine
|
57
|
+
|
58
|
+
gem 'bundler', :version => ">=0.9"
|
59
|
+
|
60
|
+
gem 'isolate', :version => ">=1.3.0"
|
61
|
+
|
62
|
+
gem 'rake', :version => ">=0.8"
|
63
|
+
|
64
|
+
gem 'passenger-nginx', :pkg => 'passenger' do
|
65
|
+
install do |shell, sudo|
|
66
|
+
|
67
|
+
shell.call "gem install passenger --no-ri --no-rdoc", :sudo => sudo
|
68
|
+
|
69
|
+
shell.call 'passenger-install-nginx-module --auto --auto-download',
|
70
|
+
:sudo => true do |stream, data, inn|
|
71
|
+
|
72
|
+
if data =~ /Please specify a prefix directory \[(.*)\]:/
|
73
|
+
|
74
|
+
dir = $1
|
75
|
+
inn.puts dir
|
76
|
+
|
77
|
+
required_dirs = [
|
78
|
+
File.join(dir, 'fastcgi_temp'),
|
79
|
+
File.join(dir, 'proxy_temp')
|
80
|
+
]
|
81
|
+
|
82
|
+
shell.call "mkdir -p #{required_dirs.join(" ")}", :sudo => true
|
83
|
+
|
84
|
+
err_log = File.join(dir, "logs/error.log")
|
85
|
+
shell.call "touch #{err_log} && chmod a+rw #{err_log}", :sudo => true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
gem 'geminstaller', :version => ">=0.5"
|
92
|
+
|
93
|
+
gem 'unicorn', :version => ">=0.9"
|
94
|
+
|
95
|
+
gem 'rainbows', :version => ">=0.90.2"
|
96
|
+
|
97
|
+
gem 'ar_mailer', :version => ">=1.5.0"
|
98
|
+
|
99
|
+
gem 'haml'
|
100
|
+
|
101
|
+
gem 'daemons'
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# DependencyLib is a simple class for building and handling depenedencies.
|
5
|
+
# A dependency tree can be defined by inheriting the DependencyLib class, and
|
6
|
+
# dependencies can be defined through dependency instantiation methods:
|
7
|
+
#
|
8
|
+
# dependency_lib.instance_eval do
|
9
|
+
#
|
10
|
+
# yum 'ruby', :pkg => 'ruby-devel'
|
11
|
+
#
|
12
|
+
# yum 'rubygems', :requires => 'ruby'
|
13
|
+
#
|
14
|
+
# gem 'rdoc', :requires => 'rubygems'
|
15
|
+
#
|
16
|
+
# gem 'ri', :requires => 'rubygems'
|
17
|
+
#
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Calling the install for rdoc will then check and install all of its parent
|
21
|
+
# dependencies as well:
|
22
|
+
#
|
23
|
+
# dependency_lib.install 'rdoc', 'ri'
|
24
|
+
#
|
25
|
+
# Dependencies may also be generic and/or have custom bash scripts
|
26
|
+
# for installs, uninstalls, and presence checks:
|
27
|
+
#
|
28
|
+
# dependency 'custom' do
|
29
|
+
# requires 'yum', 'ruby'
|
30
|
+
# install 'sudo yum install custom'
|
31
|
+
# uninstall 'sudo yum remove custom'
|
32
|
+
# check 'yum list installed custom'
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# See the Dependency class for more information.
|
36
|
+
|
37
|
+
class DependencyLib
|
38
|
+
|
39
|
+
class MissingDependency < Exception; end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Array of all dependency classes. Appended to automatically when
|
43
|
+
# DependencyLib::Dependency is inherited.
|
44
|
+
|
45
|
+
def self.dependency_types
|
46
|
+
@dependency_types ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
attr_reader :dependencies
|
51
|
+
|
52
|
+
def initialize
|
53
|
+
@dependencies = Hash.new
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
##
|
58
|
+
# Returns a dependency hash by type:
|
59
|
+
# DependencyLib['name'] #=> {:yum => <Yum...>, :apt => <Apt...>, ...}
|
60
|
+
|
61
|
+
def [](key)
|
62
|
+
@dependencies[key]
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
##
|
67
|
+
# Add a dependency to the dependencies hash.
|
68
|
+
|
69
|
+
def add dep
|
70
|
+
(@dependencies[dep.name] ||= []).unshift dep
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
##
|
75
|
+
# Checks for the existance of a dependency by name
|
76
|
+
|
77
|
+
def exist? key
|
78
|
+
@dependencies.has_key? key
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
##
|
83
|
+
# Get a dependency object by name. Supports passing :type => :pkg_manager
|
84
|
+
# if dependencies with the same name but different package managers exist:
|
85
|
+
# dependencies.get 'daemon', :type => Gem
|
86
|
+
# #=> <Gem @name="daemon"...>
|
87
|
+
#
|
88
|
+
# For an 'nginx' dependency defined for both apt and yum, where the yum
|
89
|
+
# dependency object was added to the tree last. Returns nil if
|
90
|
+
# no matching dependency type is found:
|
91
|
+
# dependencies.get 'nginx'
|
92
|
+
# #=> <Yum @name="nginx"...>
|
93
|
+
#
|
94
|
+
# dependencies.get 'nginx', :type => Apt
|
95
|
+
# #=> <Apt @name="nginx"...>
|
96
|
+
#
|
97
|
+
# Use the :prefer option if a certain dependency type is prefered but
|
98
|
+
# will fall back to whatever first dependency is available:
|
99
|
+
# dependencies.yum 'my_dep'
|
100
|
+
# dependencies.get 'my_dep', :prefer => Apt
|
101
|
+
# #=> <Yum @name="my_dep"...>
|
102
|
+
#
|
103
|
+
# Both the :type and the :prefer options support passing arrays to search
|
104
|
+
# from best to least acceptable candidate:
|
105
|
+
# dependencies.yum 'my_dep'
|
106
|
+
# dependencies.apt 'my_dep'
|
107
|
+
# dependencies.get 'my_dep', :type => [Tpkg, Yum]
|
108
|
+
# #=> <Yum @name="my_dep"...>
|
109
|
+
|
110
|
+
def get name, options={}
|
111
|
+
return unless exist? name
|
112
|
+
|
113
|
+
deps = @dependencies[name]
|
114
|
+
dep_types = [*(options[:type] || options[:prefer])].compact
|
115
|
+
|
116
|
+
return deps.first if dep_types.empty?
|
117
|
+
|
118
|
+
dep_types.each do |dep_type|
|
119
|
+
deps.each do |dep|
|
120
|
+
return dep if dep_type === dep
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
return deps.first unless options[:type]
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
##
|
129
|
+
# Install one or more dependencies:
|
130
|
+
#
|
131
|
+
# dependencies.install 'dep1', 'dep2', options_hash
|
132
|
+
#
|
133
|
+
# See DependencyLib#get and Dependency#install! for supported options.
|
134
|
+
#
|
135
|
+
# Note: If a Dependency object is passed and the :type option is set,
|
136
|
+
# DependencyLib will attempt to find and install a dependency of class :type
|
137
|
+
# with the same name as the passed Dependency object:
|
138
|
+
# my_dep = dependencies.yum "my_dep_yum_only"
|
139
|
+
# dependencies.install my_dep, :type => Apt
|
140
|
+
# #=> "No dependency 'my_dep' [Sunshine::Apt]"
|
141
|
+
|
142
|
+
def install(*deps)
|
143
|
+
send_each(:install!, *deps)
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
##
|
148
|
+
# Uninstall one or more dependencies:
|
149
|
+
#
|
150
|
+
# dependencies.uninstall 'dep1', 'dep2', options_hash
|
151
|
+
#
|
152
|
+
# See DependencyLib#get and Dependency#uninstall! for supported options.
|
153
|
+
|
154
|
+
def uninstall(*deps)
|
155
|
+
send_each(:uninstall!, *deps)
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
##
|
160
|
+
# Get and call method on each dependency passed
|
161
|
+
|
162
|
+
def send_each(method, *deps)
|
163
|
+
options = Hash === deps.last ? deps.delete_at(-1).dup : {}
|
164
|
+
|
165
|
+
#if options[:call].respond_to? :pkg_manager
|
166
|
+
# options[:prefer] ||= options[:call].pkg_manager
|
167
|
+
#end
|
168
|
+
|
169
|
+
deps.each do |dep_name|
|
170
|
+
dep = if Dependency === dep_name
|
171
|
+
if options[:type] && !(options[:type] === dep_name)
|
172
|
+
get(dep_name.name, options)
|
173
|
+
else
|
174
|
+
dep_name
|
175
|
+
end
|
176
|
+
else
|
177
|
+
get(dep_name, options)
|
178
|
+
end
|
179
|
+
|
180
|
+
raise MissingDependency,
|
181
|
+
"No dependency '#{dep_name}' [#{options[:type] || "any"}]" if !dep
|
182
|
+
|
183
|
+
# Remove :type so dependencies of other types than dep can be installed
|
184
|
+
options.delete(:type)
|
185
|
+
|
186
|
+
dep.send method, options
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
##
|
192
|
+
# Define if sudo should be used
|
193
|
+
|
194
|
+
def self.sudo= value
|
195
|
+
dependency_types.each do |dep_class|
|
196
|
+
dep_class.sudo = value
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# A standard sunshine exception
|
5
|
+
class Exception < StandardError
|
6
|
+
def initialize input=nil, message=nil
|
7
|
+
if ::Exception === input
|
8
|
+
message = [message, input.message].compact.join(": ")
|
9
|
+
super(message)
|
10
|
+
self.set_backtrace(input.backtrace)
|
11
|
+
else
|
12
|
+
super(input)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
# An error occurred when attempting to run a command on the local system
|
20
|
+
class CmdError < Exception; end
|
21
|
+
|
22
|
+
|
23
|
+
##
|
24
|
+
# An ssh call returned a non-zero exit code
|
25
|
+
class SSHCmdError < CmdError
|
26
|
+
attr_reader :shell
|
27
|
+
def initialize message=nil, shell=nil
|
28
|
+
@shell = shell
|
29
|
+
super(message)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
##
|
35
|
+
# Something went wrong with a deploy-specific item.
|
36
|
+
class DeployError < Exception; end
|
37
|
+
|
38
|
+
|
39
|
+
##
|
40
|
+
# The error is serious enough that deploy cannot proceed.
|
41
|
+
# Sunshine will attempt to revert to a previous deploy if available.
|
42
|
+
class CriticalDeployError < DeployError; end
|
43
|
+
|
44
|
+
|
45
|
+
##
|
46
|
+
# The error is so serious that all no more action can be taken.
|
47
|
+
# Sunshine will attempt to close any ssh connections and stop the deploy.
|
48
|
+
class FatalDeployError < DeployError; end
|
49
|
+
|
50
|
+
##
|
51
|
+
# A dependency could not be installed.
|
52
|
+
class DependencyError < FatalDeployError; end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Healthcheck objects handle enabling and disabling health checking for
|
5
|
+
# load balancers by touching health.enabled and health.disabled files on
|
6
|
+
# an app's shell.
|
7
|
+
|
8
|
+
class Healthcheck
|
9
|
+
|
10
|
+
ENABLED_FILE = "health.enabled"
|
11
|
+
DISABLED_FILE = "health.disabled"
|
12
|
+
|
13
|
+
attr_accessor :shell, :enabled_file, :disabled_file
|
14
|
+
|
15
|
+
def initialize path, shell
|
16
|
+
@shell = shell
|
17
|
+
@enabled_file = File.join path, ENABLED_FILE
|
18
|
+
@disabled_file = File.join path, DISABLED_FILE
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
##
|
23
|
+
# Disables healthcheck - status: :disabled
|
24
|
+
|
25
|
+
def disable
|
26
|
+
@shell.call "touch #{@disabled_file} && rm -f #{@enabled_file}"
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
##
|
31
|
+
# Check if healthcheck is disabled.
|
32
|
+
|
33
|
+
def disabled?
|
34
|
+
@shell.file? @disabled_file
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
##
|
39
|
+
# Check if healthcheck is down.
|
40
|
+
|
41
|
+
def down?
|
42
|
+
!@shell.file?(@disabled_file) && !@shell.file?(@enabled_file)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
##
|
47
|
+
# Enables healthcheck which should set status to :ok
|
48
|
+
|
49
|
+
def enable
|
50
|
+
@shell.call "rm -f #{@disabled_file} && touch #{@enabled_file}"
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
##
|
55
|
+
# Check if healthcheck is enabled.
|
56
|
+
|
57
|
+
def enabled?
|
58
|
+
@shell.file? @enabled_file
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
##
|
63
|
+
# Remove the healthcheck file - status: :down
|
64
|
+
|
65
|
+
def remove
|
66
|
+
@shell.call "rm -f #{@disabled_file} #{@enabled_file}"
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
##
|
71
|
+
# Get the health status from the shell.
|
72
|
+
# Returns one of three states:
|
73
|
+
# :enabled: everything is great
|
74
|
+
# :disabled: healthcheck was explicitely turned off
|
75
|
+
# :down: um, something is wrong
|
76
|
+
|
77
|
+
def status
|
78
|
+
return :disabled if disabled?
|
79
|
+
return :enabled if enabled?
|
80
|
+
:down
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|