sunshine 1.0.3 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +22 -2
- data/Manifest.txt +7 -0
- data/README.txt +333 -57
- data/Rakefile +1 -1
- data/lib/commands/add.rb +2 -2
- data/lib/commands/default.rb +15 -8
- data/lib/commands/list.rb +5 -3
- data/lib/commands/restart.rb +2 -2
- data/lib/commands/rm.rb +2 -2
- data/lib/commands/run.rb +2 -2
- data/lib/commands/start.rb +2 -2
- data/lib/commands/stop.rb +2 -2
- data/lib/sunshine.rb +117 -132
- data/lib/sunshine/app.rb +116 -10
- data/lib/sunshine/crontab.rb +11 -2
- data/lib/sunshine/daemon.rb +60 -46
- data/lib/sunshine/daemons/apache.rb +10 -2
- data/lib/sunshine/daemons/ar_sendmail.rb +0 -6
- data/lib/sunshine/daemons/delayed_job.rb +2 -0
- data/lib/sunshine/daemons/mongrel_rails.rb +32 -0
- data/lib/sunshine/daemons/nginx.rb +3 -0
- data/lib/sunshine/daemons/rainbows.rb +2 -0
- data/lib/sunshine/daemons/server.rb +51 -24
- data/lib/sunshine/daemons/server_cluster.rb +47 -0
- data/lib/sunshine/daemons/thin.rb +36 -0
- data/lib/sunshine/daemons/unicorn.rb +4 -1
- data/lib/sunshine/dependencies.rb +10 -3
- data/lib/sunshine/healthcheck.rb +2 -2
- data/lib/sunshine/remote_shell.rb +11 -2
- data/lib/sunshine/repo.rb +1 -1
- data/lib/sunshine/repos/rsync_repo.rb +1 -0
- data/templates/apache/apache.conf.erb +25 -18
- data/templates/mongrel_rails/mongrel_rails.conf.erb +9 -0
- data/templates/nginx/nginx.conf.erb +12 -9
- data/templates/thin/thin.conf.erb +12 -0
- data/test/helper_methods.rb +161 -0
- data/test/unit/test_daemon.rb +1 -8
- data/test/unit/test_nginx.rb +1 -1
- data/test/unit/test_server.rb +16 -0
- data/test/unit/test_server_cluster.rb +46 -0
- data/test/unit/test_sunshine.rb +18 -12
- metadata +14 -11
data/lib/sunshine/app.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
module Sunshine
|
2
2
|
|
3
3
|
##
|
4
|
-
# App objects are the core of
|
4
|
+
# App objects are the core of Sunshine deployment. The Sunshine paradygm
|
5
5
|
# is to construct an app object, and run custom deploy code by passing
|
6
6
|
# a block to its deploy method:
|
7
7
|
#
|
8
|
-
#
|
8
|
+
# someserver = Sunshine::RemoteShell.new "user@someserver.com",
|
9
|
+
# :roles => [:web, :app]
|
10
|
+
#
|
11
|
+
# options = {
|
9
12
|
# :name => 'myapp',
|
10
13
|
# :repo => {:type => :svn, :url => 'svn://blah...'},
|
11
14
|
# :root_path => '/usr/local/myapp',
|
12
|
-
# :remote_shells =>
|
15
|
+
# :remote_shells => 'user@someserver.com'
|
13
16
|
# }
|
14
17
|
#
|
15
18
|
# app = Sunshine::App.new(options)
|
@@ -19,10 +22,12 @@ module Sunshine
|
|
19
22
|
# app_server = Sunshine::Rainbows.new(app)
|
20
23
|
# app_server.restart
|
21
24
|
#
|
22
|
-
# Sunshine::Nginx.new(app, :point_to => app_server).
|
25
|
+
# Sunshine::Nginx.new(app, :point_to => app_server).setup
|
23
26
|
#
|
24
27
|
# end
|
25
28
|
#
|
29
|
+
# app.start :force => true
|
30
|
+
#
|
26
31
|
# Multiple apps can be defined, and deployed from a single deploy script.
|
27
32
|
# The constructor also supports passing a yaml file path:
|
28
33
|
#
|
@@ -31,8 +36,104 @@ module Sunshine
|
|
31
36
|
# Deployment can be expressed more concisely by calling App::deploy:
|
32
37
|
#
|
33
38
|
# App.deploy("path/to/config.yml") do |app|
|
34
|
-
# Sunshine::Rainbows.new(app).
|
39
|
+
# Sunshine::Rainbows.new(app).setup
|
35
40
|
# end
|
41
|
+
#
|
42
|
+
#
|
43
|
+
# An App holds information about where to deploy an application to and
|
44
|
+
# how to deploy it, as well as many convenience methods to setup and
|
45
|
+
# manipulate the deployment process. Most of these methods support passing
|
46
|
+
# remote shell find options:
|
47
|
+
#
|
48
|
+
# app.rake 'db:migrate', :role => :db
|
49
|
+
# app.deploy :host => 'server1.com'
|
50
|
+
#
|
51
|
+
# See App#find for more information.
|
52
|
+
#
|
53
|
+
# App instantiation can be done in several ways:
|
54
|
+
# App.new instantiation_hash
|
55
|
+
# App.new "path/to/config.yml", optional_extra_hash
|
56
|
+
# App.new #=> will attempt to load ruby's file DATA as yaml
|
57
|
+
#
|
58
|
+
# Yaml files must define settings on a per-environment basis. The default
|
59
|
+
# environment will be used if the deploy_env is not found in the config.
|
60
|
+
# Let's consider the following config:
|
61
|
+
#
|
62
|
+
# #config.yml:
|
63
|
+
# ---
|
64
|
+
# :default:
|
65
|
+
# :repo:
|
66
|
+
# :type: :svn
|
67
|
+
# :url: http://subversion/repo/tags/release-001
|
68
|
+
# :remote_shells: dev.myserver.com
|
69
|
+
#
|
70
|
+
# :qa:
|
71
|
+
# :remote_shells:
|
72
|
+
# - qa1.myserver.com
|
73
|
+
# - qa2.myserver.com
|
74
|
+
#
|
75
|
+
# :qa_special:
|
76
|
+
# :inherits: :qa
|
77
|
+
# :root_path: /path/to/application
|
78
|
+
#
|
79
|
+
# By default, environment definitions inherit the :default environment. In
|
80
|
+
# this instance, :qa_special also inherits from :qa.
|
81
|
+
# With the given config, I could setup the App instance as so:
|
82
|
+
#
|
83
|
+
# App.new "config.yml", :deploy_env => :development
|
84
|
+
# # Note: by default, App will get the deploy_env value
|
85
|
+
# # from Sunshine.deploy_env
|
86
|
+
#
|
87
|
+
# The above will simply load the default config. The following, however,
|
88
|
+
# will load the :qa_special config which inherits from
|
89
|
+
# both :qa and :default:
|
90
|
+
#
|
91
|
+
# App.new "config.yml", :deploy_env => :qa_special
|
92
|
+
#
|
93
|
+
#
|
94
|
+
# Another way of instantiating an App is to pass it a hash. Unlike the yaml
|
95
|
+
# config file, the hash is not on a per-environment basis and isexpected
|
96
|
+
# to already have the correct values for the given environment.
|
97
|
+
# The following is equivalent to loading the above :default environment:
|
98
|
+
#
|
99
|
+
# App.new :remote_shells => "dev.myserver.com",
|
100
|
+
# :repo => {
|
101
|
+
# :type => :svn,
|
102
|
+
# :url => "http://subversion/repo/tags/release-001"
|
103
|
+
# }
|
104
|
+
#
|
105
|
+
# In theory, the minimum amount of information required to instantiate
|
106
|
+
# an app is the repo and remote_shells. If the repo option is omitted,
|
107
|
+
# the App will attempt to detect if the pwd is a checkout out repo and
|
108
|
+
# use that information. If you would like to deploy an application that
|
109
|
+
# is not under source countrol, you may do so by using Sunshine::RsyncRepo,
|
110
|
+
# or passing :rsync in your hash as your repo type.
|
111
|
+
#
|
112
|
+
#
|
113
|
+
# Options supported by App.new are the following:
|
114
|
+
#
|
115
|
+
# :deploy_env:: String - specify the env to deploy with; defaults to
|
116
|
+
# Sunshine#deploy_env.
|
117
|
+
#
|
118
|
+
# :deploy_name:: String - if you want to specify a name for your deploy and
|
119
|
+
# checkout directory (affects the checkout_path); defaults to Time.now.to_i.
|
120
|
+
#
|
121
|
+
# :remote_shells:: String|Array|Sunshine::Shell - the shell(s) to use for
|
122
|
+
# deployment. Accepts any single instance or array of a Sunshine::Shell
|
123
|
+
# type instance or Sunshine::Shell instantiator-friendly arguments.
|
124
|
+
#
|
125
|
+
# :repo:: Hash|Sunshine::Repo - the scm and repo to use for deployment.
|
126
|
+
# Accepts any hash that can be passed to Sunshine::Repo::new_of_type
|
127
|
+
# or any Sunshine::Repo object.
|
128
|
+
#
|
129
|
+
# :root_path:: String - the absolute path the deployed application
|
130
|
+
# should live in; defaults to "#{Sunshine.web_directory}/#{@name}".
|
131
|
+
#
|
132
|
+
# :shell_env:: Hash - environment variables to add to deploy shells.
|
133
|
+
#
|
134
|
+
# :sudo:: true|false|nil|String - which sudo value should be assigned to
|
135
|
+
# deploy shells; defaults to Sunshine#sudo. For more information on using
|
136
|
+
# sudo, see the Using Permissions section in README.txt.
|
36
137
|
|
37
138
|
class App
|
38
139
|
|
@@ -51,6 +152,11 @@ module Sunshine
|
|
51
152
|
attr_reader :root_path, :checkout_path, :current_path, :deploys_path
|
52
153
|
attr_reader :shared_path, :log_path, :deploy_name, :deploy_env
|
53
154
|
|
155
|
+
##
|
156
|
+
# App instantiation can be done in several ways:
|
157
|
+
# App.new instantiation_hash
|
158
|
+
# App.new "path/to/config.yml", optional_extra_hash
|
159
|
+
# App.new #=> will attempt to load ruby's file DATA as yaml
|
54
160
|
|
55
161
|
def initialize config_file=Sunshine::DATA, options={}
|
56
162
|
options, config_file = config_file, Sunshine::DATA if Hash === config_file
|
@@ -833,13 +939,13 @@ module Sunshine
|
|
833
939
|
# server_apps_from_config ["svr1", "svr2", "svr3"]
|
834
940
|
# #=> [<ServerApp @host="svr1">,<ServerApp @host="svr2">, ...]
|
835
941
|
#
|
836
|
-
#
|
837
|
-
# server_apps_from_config
|
942
|
+
# remote_shells = [["svr1", {:roles => "web db app"}], "svr2", "svr3"]
|
943
|
+
# server_apps_from_config remote_shells
|
838
944
|
# #=> [<ServerApp @host="svr1">,<ServerApp @host="svr2">, ...]
|
839
945
|
|
840
|
-
def server_apps_from_config
|
841
|
-
|
842
|
-
|
946
|
+
def server_apps_from_config shells
|
947
|
+
shells = [*shells].compact
|
948
|
+
shells.map{|shell| ServerApp.new(*[self,*shell]) }
|
843
949
|
end
|
844
950
|
|
845
951
|
|
data/lib/sunshine/crontab.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Sunshine
|
2
2
|
|
3
3
|
##
|
4
|
-
# A simple namespaced grouping of cron jobs that can be written
|
5
|
-
# to a
|
4
|
+
# A simple namespaced grouping of cron jobs that can be read and written
|
5
|
+
# to a shell.
|
6
6
|
|
7
7
|
class Crontab
|
8
8
|
|
@@ -117,11 +117,17 @@ module Sunshine
|
|
117
117
|
|
118
118
|
private
|
119
119
|
|
120
|
+
##
|
121
|
+
# Write the string to the shell's crontab.
|
122
|
+
|
120
123
|
def write_crontab content
|
121
124
|
@shell.call("echo '#{content.gsub(/'/){|s| "'\\''"}}' | crontab")
|
122
125
|
end
|
123
126
|
|
124
127
|
|
128
|
+
##
|
129
|
+
# Deletes all jobs of a given namespace in a crontab string.
|
130
|
+
|
125
131
|
def delete_jobs crontab, namespace=nil
|
126
132
|
start_id, end_id = get_job_ids namespace
|
127
133
|
|
@@ -131,6 +137,9 @@ module Sunshine
|
|
131
137
|
end
|
132
138
|
|
133
139
|
|
140
|
+
##
|
141
|
+
# Returns the cronjob begin and end flags.
|
142
|
+
|
134
143
|
def get_job_ids namespace=nil
|
135
144
|
namespace ||= "[^\n]*"
|
136
145
|
|
data/lib/sunshine/daemon.rb
CHANGED
@@ -17,8 +17,17 @@ module Sunshine
|
|
17
17
|
# for template rendering.
|
18
18
|
|
19
19
|
def self.binder_methods
|
20
|
-
[:app, :name, :
|
21
|
-
|
20
|
+
[:app, :name, :bin, :pid, :processes, :config_path, :log_file, :timeout]
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
##
|
25
|
+
# Returns the short, snake-case version of the class:
|
26
|
+
# Sunshine::Daemon.short_name
|
27
|
+
# #=> "daemon"
|
28
|
+
|
29
|
+
def self.short_name
|
30
|
+
@short_name ||= self.underscore self.to_s.split("::").last
|
22
31
|
end
|
23
32
|
|
24
33
|
|
@@ -31,7 +40,7 @@ module Sunshine
|
|
31
40
|
end
|
32
41
|
|
33
42
|
|
34
|
-
attr_reader :app, :name
|
43
|
+
attr_reader :app, :name
|
35
44
|
|
36
45
|
attr_accessor :bin, :pid, :processes, :timeout, :sudo, :server_apps
|
37
46
|
|
@@ -43,34 +52,31 @@ module Sunshine
|
|
43
52
|
# Daemon objects need only an App object to be instantiated but many options
|
44
53
|
# are available for customization:
|
45
54
|
#
|
46
|
-
# :
|
47
|
-
#
|
48
|
-
#
|
49
|
-
# :bin:: bin_path - set the daemon app bin path (e.g. usr/local/nginx)
|
50
|
-
# defaults to svr_name
|
55
|
+
# :bin:: bin_path - Set the daemon app bin path (e.g. usr/local/nginx)
|
56
|
+
# defaults to svr_name.
|
51
57
|
#
|
52
|
-
# :
|
58
|
+
# :processes:: prcss_num - Number of processes daemon should run;
|
59
|
+
# defaults to 1.
|
53
60
|
#
|
54
|
-
# :
|
55
|
-
#
|
61
|
+
# :config_file:: name - Remote file name the daemon should load;
|
62
|
+
# defaults to svr_name.conf
|
56
63
|
#
|
57
|
-
# :
|
58
|
-
#
|
64
|
+
# :config_path:: path - Remote path daemon configs will be uploaded to;
|
65
|
+
# defaults to app.current_path/daemons/svr_name
|
59
66
|
#
|
60
|
-
# :config_template:: path -
|
61
|
-
#
|
67
|
+
# :config_template:: path - Glob path to tempates to render and upload;
|
68
|
+
# defaults to sunshine_path/templates/svr_name/*
|
62
69
|
#
|
63
|
-
# :
|
64
|
-
#
|
70
|
+
# :log_path:: path - Path to where the log files should be output;
|
71
|
+
# defaults to app.log_path.
|
65
72
|
#
|
66
|
-
# :
|
67
|
-
#
|
73
|
+
# :pid:: pid_path - Set the pid; default: app.shared_path/pids/svr_name.pid
|
74
|
+
# defaults to app.shared_path/pids/svr_name.pid.
|
68
75
|
#
|
69
|
-
# :
|
70
|
-
#
|
76
|
+
# :sudo:: bool|str - Define if sudo should be used to run the daemon,
|
77
|
+
# and/or with what user.
|
71
78
|
#
|
72
|
-
# :
|
73
|
-
# defaults to the passed app
|
79
|
+
# :timeout:: int - Timeout to use for daemon config, defaults to 0.
|
74
80
|
#
|
75
81
|
# The Daemon constructor also supports any App#find options to narrow
|
76
82
|
# the server apps to use. Note: subclasses such as Server already have
|
@@ -78,27 +84,24 @@ module Sunshine
|
|
78
84
|
|
79
85
|
def initialize app, options={}
|
80
86
|
@options = options
|
87
|
+
@app = app
|
81
88
|
|
82
|
-
@
|
83
|
-
@
|
84
|
-
|
85
|
-
@short_class_name = self.class.underscore self.class.to_s.split("::").last
|
86
|
-
|
87
|
-
@name = options[:name] || @short_class_name
|
88
|
-
|
89
|
-
@pid = options[:pid] || "#{@app.shared_path}/pids/#{@name}.pid"
|
90
|
-
@bin = options[:bin] || @name
|
89
|
+
@name = options[:name] || self.class.short_name
|
90
|
+
@pid = options[:pid] || "#{@app.shared_path}/pids/#{@name}.pid"
|
91
|
+
@bin = options[:bin] || self.class.short_name
|
91
92
|
@sudo = options[:sudo]
|
92
|
-
@timeout = options[:timeout]
|
93
|
-
@dep_name = options[:dep_name]
|
93
|
+
@timeout = options[:timeout] || 0
|
94
|
+
@dep_name = options[:dep_name] || self.class.short_name
|
94
95
|
@processes = options[:processes] || 1
|
96
|
+
@sigkill = 'QUIT'
|
95
97
|
|
96
|
-
@config_template
|
97
|
-
|
98
|
+
@config_template = options[:config_template] ||
|
99
|
+
"#{Sunshine::ROOT}/templates/#{self.class.short_name}/*"
|
98
100
|
|
99
101
|
@config_path = options[:config_path] ||
|
100
102
|
"#{@app.current_path}/daemons/#{@name}"
|
101
|
-
|
103
|
+
|
104
|
+
@config_file = options[:config_file] || "#{self.class.short_name}.conf"
|
102
105
|
|
103
106
|
log_path = options[:log_path] || @app.log_path
|
104
107
|
@log_files = {
|
@@ -195,6 +198,20 @@ module Sunshine
|
|
195
198
|
end
|
196
199
|
|
197
200
|
|
201
|
+
##
|
202
|
+
# Check if the daemon is running on all servers
|
203
|
+
|
204
|
+
def status
|
205
|
+
each_server_app do |server_app|
|
206
|
+
server_app.shell.call status_cmd, :sudo => pick_sudo(server_app.shell)
|
207
|
+
end
|
208
|
+
true
|
209
|
+
|
210
|
+
rescue CmdError => e
|
211
|
+
false
|
212
|
+
end
|
213
|
+
|
214
|
+
|
198
215
|
##
|
199
216
|
# Stop the daemon app.
|
200
217
|
|
@@ -248,12 +265,11 @@ module Sunshine
|
|
248
265
|
|
249
266
|
|
250
267
|
##
|
251
|
-
#
|
252
|
-
# Should be overridden by child classes.
|
268
|
+
# Default daemon stop command.
|
253
269
|
|
254
270
|
def stop_cmd
|
255
|
-
|
256
|
-
|
271
|
+
"test -f #{@pid} && kill -#{@sigkill} $(cat #{@pid}) && sleep 1 && "+
|
272
|
+
"rm -f #{@pid} || echo 'Could not kill #{@name} pid for #{@app.name}';"
|
257
273
|
end
|
258
274
|
|
259
275
|
|
@@ -304,7 +320,7 @@ module Sunshine
|
|
304
320
|
# Upload config files and run them through erb with the provided
|
305
321
|
# binding if necessary.
|
306
322
|
|
307
|
-
def upload_config_files
|
323
|
+
def upload_config_files shell, setup_binding=binding
|
308
324
|
config_template_files.each do |config_file|
|
309
325
|
|
310
326
|
if File.extname(config_file) == ".erb"
|
@@ -343,10 +359,6 @@ module Sunshine
|
|
343
359
|
shell.expand_path path
|
344
360
|
end
|
345
361
|
|
346
|
-
binder.set :target_server do
|
347
|
-
target.server_name || server_name
|
348
|
-
end
|
349
|
-
|
350
362
|
binder
|
351
363
|
end
|
352
364
|
|
@@ -399,6 +411,8 @@ module Sunshine
|
|
399
411
|
|
400
412
|
def register_after_user_script
|
401
413
|
@app.after_user_script do |app|
|
414
|
+
next unless has_setup?
|
415
|
+
|
402
416
|
each_server_app do |sa|
|
403
417
|
sudo = pick_sudo sa.shell
|
404
418
|
|
@@ -2,6 +2,10 @@ module Sunshine
|
|
2
2
|
|
3
3
|
##
|
4
4
|
# A wrapper for configuring the apache2 server.
|
5
|
+
# Note: Due to Apache default limitations, the @connections attribute
|
6
|
+
# defaults to 256.
|
7
|
+
#
|
8
|
+
# Note: The minimum timeout supported by Apache is 1 second.
|
5
9
|
|
6
10
|
class Apache < Server
|
7
11
|
|
@@ -12,8 +16,12 @@ module Sunshine
|
|
12
16
|
|
13
17
|
@sigkill = 'WINCH'
|
14
18
|
|
15
|
-
|
16
|
-
@
|
19
|
+
@supports_rack = false
|
20
|
+
@supports_passenger = true
|
21
|
+
|
22
|
+
@connections = options[:connections] || 256
|
23
|
+
|
24
|
+
@timeout = 1 if @timeout < 1
|
17
25
|
|
18
26
|
@dep_name = options[:dep_name] ||
|
19
27
|
use_passenger? ? 'passenger-apache' : 'apache2'
|
@@ -18,11 +18,5 @@ module Sunshine
|
|
18
18
|
def start_cmd
|
19
19
|
"cd #{@app.current_path} && #{@bin} -p #{@pid} -d"
|
20
20
|
end
|
21
|
-
|
22
|
-
|
23
|
-
def stop_cmd
|
24
|
-
"test -f #{@pid} && kill `cat #{@pid}` || "+
|
25
|
-
"echo 'No #{@name} process to stop for #{@app.name}'; rm -f #{@pid};"
|
26
|
-
end
|
27
21
|
end
|
28
22
|
end
|
@@ -4,6 +4,8 @@ module Sunshine
|
|
4
4
|
# Simple daemon wrapper for delayed_job daemon setup and control.
|
5
5
|
# By default, uses server apps with the :dj role. Supports
|
6
6
|
# the :processes option.
|
7
|
+
#
|
8
|
+
# Note: The pid location is fixed at [current_path]/tmp/pids/delayed_job.pid"
|
7
9
|
|
8
10
|
class DelayedJob < Daemon
|
9
11
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Simple wrapper around mongrel setup and commands. For clustering,
|
5
|
+
# look at using Sunshine's ServerCluster method, e.g.:
|
6
|
+
# MongrelRails.new_cluster 10, app, :port => 5000
|
7
|
+
#
|
8
|
+
# Note: This is a rails-only server, implemented for compatibility
|
9
|
+
# with older non-rack rails applications. Consider upgrading your
|
10
|
+
# application to run on rack and/or thin.
|
11
|
+
#
|
12
|
+
# Note: Mongrel only supports a single log file.
|
13
|
+
# The default stdout file is used.
|
14
|
+
|
15
|
+
class MongrelRails < Server
|
16
|
+
|
17
|
+
def initialize app, options={}
|
18
|
+
super
|
19
|
+
|
20
|
+
@dep_name = options[:dep_name] || "mongrel"
|
21
|
+
|
22
|
+
@supports_rack = false
|
23
|
+
@supports_passenger = false
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
def start_cmd
|
28
|
+
"cd #{@app.current_path} && mongrel_rails start "+
|
29
|
+
"-C #{self.config_file_path}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|