sunshine 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
|