sunshine 1.0.3 → 1.1.0
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 +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
|