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,70 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Instantiated per deploy server and used to pass bindings to the config's
|
5
|
+
# ERB build method:
|
6
|
+
# binder.set :server_name, "blah.com"
|
7
|
+
# binder.forward :server_method, ...
|
8
|
+
# binder.get_binding
|
9
|
+
|
10
|
+
class Binder
|
11
|
+
|
12
|
+
def initialize target
|
13
|
+
@target = target
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
##
|
18
|
+
# Set the binding instance variable and accessor method.
|
19
|
+
|
20
|
+
def set key, value=nil, &block
|
21
|
+
value ||= block if block_given?
|
22
|
+
|
23
|
+
instance_variable_set("@#{key}", value)
|
24
|
+
|
25
|
+
eval_str = <<-STR
|
26
|
+
undef #{key} if defined?(#{key})
|
27
|
+
def #{key}(*args)
|
28
|
+
if Proc === @#{key}
|
29
|
+
@#{key}.call(*args)
|
30
|
+
else
|
31
|
+
@#{key}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
STR
|
35
|
+
|
36
|
+
instance_eval eval_str, __FILE__, __LINE__ + 1
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
##
|
41
|
+
# Takes a hash and assign each hash key/value as an attribute.
|
42
|
+
|
43
|
+
def import_hash hash
|
44
|
+
hash.each{|k, v| self.set(k, v)}
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
##
|
49
|
+
# Forward a method to the server instance.
|
50
|
+
|
51
|
+
def forward *method_names
|
52
|
+
method_names.each do |method_name|
|
53
|
+
instance_eval <<-STR, __FILE__, __LINE__ + 1
|
54
|
+
undef #{method_name} if defined?(#{method_name})
|
55
|
+
def #{method_name}(*args, &block)
|
56
|
+
@target.#{method_name}(*args, &block)
|
57
|
+
end
|
58
|
+
STR
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
##
|
64
|
+
# Retrieve the object's binding.
|
65
|
+
|
66
|
+
def get_binding
|
67
|
+
binding
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# A simple namespaced grouping of cron jobs that can be written
|
5
|
+
# to a deploy server.
|
6
|
+
|
7
|
+
class Crontab
|
8
|
+
|
9
|
+
attr_reader :name, :shell
|
10
|
+
|
11
|
+
def initialize name, shell
|
12
|
+
@name = name
|
13
|
+
@shell = shell
|
14
|
+
@jobs = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
# Get the jobs matching this crontab. Loads them from the crontab
|
20
|
+
# if @jobs hasn't been set yet.
|
21
|
+
|
22
|
+
def jobs
|
23
|
+
@jobs ||= parse read_crontab
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
##
|
28
|
+
# Build the crontab by replacing preexisting cron jobs and adding new ones.
|
29
|
+
|
30
|
+
def build crontab=""
|
31
|
+
crontab.strip!
|
32
|
+
|
33
|
+
jobs.each do |namespace, cron_job|
|
34
|
+
crontab = delete_jobs crontab, namespace
|
35
|
+
|
36
|
+
start_id, end_id = get_job_ids namespace
|
37
|
+
cron_str = "\n#{start_id}\n#{cron_job.chomp}\n#{end_id}\n\n"
|
38
|
+
|
39
|
+
crontab << cron_str
|
40
|
+
end
|
41
|
+
|
42
|
+
crontab
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
##
|
47
|
+
# Remove all cron jobs that reference crontab.name.
|
48
|
+
|
49
|
+
def delete!
|
50
|
+
crontab = read_crontab
|
51
|
+
crontab = delete_jobs crontab
|
52
|
+
|
53
|
+
write_crontab crontab
|
54
|
+
|
55
|
+
crontab
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
##
|
60
|
+
# Write the crontab on the given shell
|
61
|
+
|
62
|
+
def write!
|
63
|
+
return unless modified?
|
64
|
+
|
65
|
+
crontab = read_crontab
|
66
|
+
crontab = delete_jobs crontab
|
67
|
+
crontab = build crontab
|
68
|
+
|
69
|
+
write_crontab crontab
|
70
|
+
|
71
|
+
crontab
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
##
|
76
|
+
# Checks if the crontab was modified for crontab.name.
|
77
|
+
|
78
|
+
def modified?
|
79
|
+
!@jobs.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
##
|
84
|
+
# Load a crontab string and parse out jobs related to crontab.name.
|
85
|
+
# Returns a hash of namespace/jobs pairs.
|
86
|
+
|
87
|
+
def parse string
|
88
|
+
jobs = Hash.new
|
89
|
+
|
90
|
+
namespace = nil
|
91
|
+
|
92
|
+
string.each_line do |line|
|
93
|
+
if line =~ /^# sunshine #{@name}:(.*):begin/
|
94
|
+
namespace = $1
|
95
|
+
next
|
96
|
+
elsif line =~ /^# sunshine #{@name}:#{namespace}:end/
|
97
|
+
namespace = nil
|
98
|
+
end
|
99
|
+
|
100
|
+
if namespace
|
101
|
+
jobs[namespace] ||= String.new
|
102
|
+
jobs[namespace] << line
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
jobs
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
##
|
111
|
+
# Returns the shell's crontab as a string
|
112
|
+
|
113
|
+
def read_crontab
|
114
|
+
@shell.call("crontab -l") rescue ""
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def write_crontab content
|
121
|
+
@shell.call("echo '#{content.gsub(/'/){|s| "'\\''"}}' | crontab")
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def delete_jobs crontab, namespace=nil
|
126
|
+
start_id, end_id = get_job_ids namespace
|
127
|
+
|
128
|
+
crontab.gsub!(/^#{start_id}$(.*?)^#{end_id}$\n*/m, "")
|
129
|
+
|
130
|
+
crontab
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def get_job_ids namespace=nil
|
135
|
+
namespace ||= "[^\n]*"
|
136
|
+
|
137
|
+
start_id = "# sunshine #{@name}:#{namespace}:begin"
|
138
|
+
end_id = "# sunshine #{@name}:#{namespace}:end"
|
139
|
+
|
140
|
+
return start_id, end_id
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,380 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# An abstract class to wrap simple daemon 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 Daemon
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
# Returns an array of method names to assign to the binder
|
17
|
+
# for template rendering.
|
18
|
+
|
19
|
+
def self.binder_methods
|
20
|
+
[:app, :name, :target, :bin, :pid, :port,
|
21
|
+
:processes, :config_path, :log_file, :timeout]
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
##
|
26
|
+
# Turn camelcase into underscore. Used for Daemon#name.
|
27
|
+
|
28
|
+
def self.underscore str
|
29
|
+
str.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
30
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
attr_reader :app, :name, :target
|
35
|
+
|
36
|
+
attr_accessor :bin, :pid, :processes, :timeout, :sudo, :server_apps
|
37
|
+
|
38
|
+
attr_accessor :config_template, :config_path, :config_file
|
39
|
+
|
40
|
+
attr_writer :start_cmd, :stop_cmd, :restart_cmd, :status_cmd
|
41
|
+
|
42
|
+
|
43
|
+
# Daemon objects need only an App object to be instantiated but many options
|
44
|
+
# are available for customization:
|
45
|
+
#
|
46
|
+
# :pid:: pid_path - set the pid; default: app.shared_path/pids/svr_name.pid
|
47
|
+
# defaults to app.shared_path/pids/svr_name.pid
|
48
|
+
#
|
49
|
+
# :bin:: bin_path - set the daemon app bin path (e.g. usr/local/nginx)
|
50
|
+
# defaults to svr_name
|
51
|
+
#
|
52
|
+
# :sudo:: bool|str - define if sudo should be used and with what user
|
53
|
+
#
|
54
|
+
# :timeout:: int|str - timeout to use for daemon config
|
55
|
+
# defaults to 1
|
56
|
+
#
|
57
|
+
# :processes:: prcss_num - number of processes daemon should run
|
58
|
+
# defaults to 0
|
59
|
+
#
|
60
|
+
# :config_template:: path - glob path to tempates to render and upload
|
61
|
+
# defaults to sunshine_path/templates/svr_name/*
|
62
|
+
#
|
63
|
+
# :config_path:: path - remote path daemon configs will be uploaded to
|
64
|
+
# defaults to app.current_path/daemons/svr_name
|
65
|
+
#
|
66
|
+
# :config_file:: name - remote file name the daemon should load
|
67
|
+
# defaults to svr_name.conf
|
68
|
+
#
|
69
|
+
# :log_path:: path - path to where the log files should be output
|
70
|
+
# defaults to app.log_path
|
71
|
+
#
|
72
|
+
# :point_to:: app|daemon - an abstract target to point to
|
73
|
+
# defaults to the passed app
|
74
|
+
#
|
75
|
+
# The Daemon constructor also supports any App#find options to narrow
|
76
|
+
# the server apps to use. Note: subclasses such as Server already have
|
77
|
+
# a default :role that can be overridden.
|
78
|
+
|
79
|
+
def initialize app, options={}
|
80
|
+
@options = options
|
81
|
+
|
82
|
+
@app = app
|
83
|
+
@target = options[:point_to] || @app
|
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
|
91
|
+
@sudo = options[:sudo]
|
92
|
+
@timeout = options[:timeout] || 0
|
93
|
+
@dep_name = options[:dep_name] || @name
|
94
|
+
@processes = options[:processes] || 1
|
95
|
+
|
96
|
+
@config_template = options[:config_template]
|
97
|
+
@config_template ||= "#{Sunshine::ROOT}/templates/#{@short_class_name}/*"
|
98
|
+
|
99
|
+
@config_path = options[:config_path] ||
|
100
|
+
"#{@app.current_path}/daemons/#{@name}"
|
101
|
+
@config_file = options[:config_file] || "#{@name}.conf"
|
102
|
+
|
103
|
+
log_path = options[:log_path] || @app.log_path
|
104
|
+
@log_files = {
|
105
|
+
:stderr => "#{log_path}/#{@name}_stderr.log",
|
106
|
+
:stdout => "#{log_path}/#{@name}_stdout.log"
|
107
|
+
}
|
108
|
+
|
109
|
+
@start_cmd = @stop_cmd = @restart_cmd = @status_cmd = nil
|
110
|
+
|
111
|
+
@setup_successful = nil
|
112
|
+
|
113
|
+
register_after_user_script
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
##
|
118
|
+
# Do something with each server app used by the daemon.
|
119
|
+
|
120
|
+
def each_server_app(&block)
|
121
|
+
@app.each(@options, &block)
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
##
|
126
|
+
# Setup the daemon, parse and upload config templates.
|
127
|
+
# If a dependency with the daemon name exists in Sunshine.dependencies,
|
128
|
+
# setup will attempt to install the dependency before uploading configs.
|
129
|
+
# If a block is given it will be passed each server_app and binder object
|
130
|
+
# which will be used for the building erb config templates.
|
131
|
+
# See the ConfigBinding class for more information.
|
132
|
+
|
133
|
+
def setup
|
134
|
+
Sunshine.logger.info @name, "Setting up #{@name} daemon" do
|
135
|
+
|
136
|
+
each_server_app do |server_app|
|
137
|
+
|
138
|
+
# Build erb binding
|
139
|
+
binder = config_binding server_app.shell
|
140
|
+
|
141
|
+
server_app.shell.call "mkdir -p #{remote_dirs.join(" ")}",
|
142
|
+
:sudo => binder.sudo
|
143
|
+
|
144
|
+
yield(server_app, binder) if block_given?
|
145
|
+
|
146
|
+
server_app.install_deps @dep_name if
|
147
|
+
Sunshine.dependencies.exist?(@dep_name)
|
148
|
+
|
149
|
+
upload_config_files(server_app.shell, binder.get_binding)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@setup_successful = true
|
154
|
+
|
155
|
+
rescue => e
|
156
|
+
raise CriticalDeployError.new(e, "Could not setup #{@name}")
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
##
|
161
|
+
# Check if setup was run successfully.
|
162
|
+
|
163
|
+
def has_setup? force=false
|
164
|
+
return @setup_successful unless @setup_successful.nil? || force
|
165
|
+
|
166
|
+
each_server_app do |server_app|
|
167
|
+
|
168
|
+
unless server_app.shell.file? config_file_path
|
169
|
+
return @setup_successful = false
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
@setup_successful = true
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
##
|
178
|
+
# Start the daemon app after running setup.
|
179
|
+
|
180
|
+
def start
|
181
|
+
self.setup unless has_setup?
|
182
|
+
Sunshine.logger.info @name, "Starting #{@name} daemon" do
|
183
|
+
|
184
|
+
each_server_app do |server_app|
|
185
|
+
begin
|
186
|
+
server_app.shell.call start_cmd,
|
187
|
+
:sudo => pick_sudo(server_app.shell)
|
188
|
+
|
189
|
+
yield(server_app) if block_given?
|
190
|
+
rescue => e
|
191
|
+
raise CriticalDeployError.new(e, "Could not start #{@name}")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
##
|
199
|
+
# Stop the daemon app.
|
200
|
+
|
201
|
+
def stop
|
202
|
+
Sunshine.logger.info @name, "Stopping #{@name} daemon" do
|
203
|
+
|
204
|
+
each_server_app do |server_app|
|
205
|
+
begin
|
206
|
+
server_app.shell.call stop_cmd,
|
207
|
+
:sudo => pick_sudo(server_app.shell)
|
208
|
+
|
209
|
+
yield(server_app) if block_given?
|
210
|
+
rescue => e
|
211
|
+
raise CriticalDeployError.new(e, "Could not stop #{@name}")
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
218
|
+
##
|
219
|
+
# Restarts the daemon using the restart_cmd attribute if provided.
|
220
|
+
# If restart_cmd is not provided, calls stop and start.
|
221
|
+
|
222
|
+
def restart
|
223
|
+
self.setup unless has_setup?
|
224
|
+
|
225
|
+
Sunshine.logger.info @name, "Restarting #{@name} daemon" do
|
226
|
+
each_server_app do |server_app|
|
227
|
+
begin
|
228
|
+
server_app.shell.call restart_cmd,
|
229
|
+
:sudo => pick_sudo(server_app.shell)
|
230
|
+
|
231
|
+
yield(server_app) if block_given?
|
232
|
+
rescue => e
|
233
|
+
raise CriticalDeployError.new(e, "Could not restart #{@name}")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
##
|
241
|
+
# Gets the command that starts the daemon.
|
242
|
+
# Should be overridden by child classes.
|
243
|
+
|
244
|
+
def start_cmd
|
245
|
+
return @start_cmd ||
|
246
|
+
raise(CriticalDeployError, "@start_cmd undefined. Can't start #{@name}")
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
##
|
251
|
+
# Gets the command that stops the daemon.
|
252
|
+
# Should be overridden by child classes.
|
253
|
+
|
254
|
+
def stop_cmd
|
255
|
+
return @stop_cmd ||
|
256
|
+
raise(CriticalDeployError, "@stop_cmd undefined. Can't stop #{@name}")
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
##
|
261
|
+
# Gets the command that restarts the daemon.
|
262
|
+
|
263
|
+
def restart_cmd
|
264
|
+
@restart_cmd || [stop_cmd, start_cmd].map{|c| "(#{c})"}.join(" && ")
|
265
|
+
end
|
266
|
+
|
267
|
+
|
268
|
+
##
|
269
|
+
# Get the command to check if the daemon is running.
|
270
|
+
|
271
|
+
def status_cmd
|
272
|
+
@status_cmd || "test -f #{@pid}"
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
##
|
277
|
+
# Append or override daemon log file paths:
|
278
|
+
# daemon.log_files :stderr => "/all_logs/stderr.log"
|
279
|
+
|
280
|
+
def log_files(hash)
|
281
|
+
@log_files.merge!(hash)
|
282
|
+
end
|
283
|
+
|
284
|
+
|
285
|
+
##
|
286
|
+
# Get the path of a log file:
|
287
|
+
# daemon.log_file(:stderr)
|
288
|
+
# #=> "/all_logs/stderr.log"
|
289
|
+
|
290
|
+
def log_file(key)
|
291
|
+
@log_files[key]
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
##
|
296
|
+
# Get the file path to the daemon's config file.
|
297
|
+
|
298
|
+
def config_file_path
|
299
|
+
"#{@config_path}/#{@config_file}"
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
##
|
304
|
+
# Upload config files and run them through erb with the provided
|
305
|
+
# binding if necessary.
|
306
|
+
|
307
|
+
def upload_config_files(shell, setup_binding=binding)
|
308
|
+
config_template_files.each do |config_file|
|
309
|
+
|
310
|
+
if File.extname(config_file) == ".erb"
|
311
|
+
filename = File.basename(config_file[0..-5])
|
312
|
+
parsed_config = @app.build_erb(config_file, setup_binding)
|
313
|
+
shell.make_file "#{@config_path}/#{filename}", parsed_config
|
314
|
+
else
|
315
|
+
filename = File.basename(config_file)
|
316
|
+
shell.upload config_file, "#{@config_path}/#{filename}"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
##
|
323
|
+
# Get the array of local config template files needed by the daemon.
|
324
|
+
|
325
|
+
def config_template_files
|
326
|
+
@config_template_files ||= Dir[@config_template].select{|f| File.file?(f)}
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
private
|
331
|
+
|
332
|
+
def config_binding shell
|
333
|
+
binder = Binder.new self
|
334
|
+
binder.forward(*self.class.binder_methods)
|
335
|
+
|
336
|
+
binder.set :shell, shell
|
337
|
+
|
338
|
+
binder_sudo = pick_sudo(shell)
|
339
|
+
binder.set :sudo, binder_sudo
|
340
|
+
|
341
|
+
binder.set :expand_path do |path|
|
342
|
+
shell.expand_path path
|
343
|
+
end
|
344
|
+
|
345
|
+
binder
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
def pick_sudo shell
|
350
|
+
case shell.sudo
|
351
|
+
when true
|
352
|
+
self.sudo || shell.sudo
|
353
|
+
when String
|
354
|
+
String === self.sudo ? self.sudo : shell.sudo
|
355
|
+
else
|
356
|
+
self.sudo
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
def remote_dirs
|
362
|
+
dirs = @log_files.values.map{|f| File.dirname(f)}
|
363
|
+
dirs.concat [@config_path, File.dirname(@pid)]
|
364
|
+
dirs.delete_if{|d| d == "."}
|
365
|
+
dirs
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
def register_after_user_script
|
370
|
+
@app.after_user_script do |app|
|
371
|
+
each_server_app do |sa|
|
372
|
+
sa.scripts[:start] << start_cmd
|
373
|
+
sa.scripts[:stop] << stop_cmd
|
374
|
+
sa.scripts[:restart] << restart_cmd
|
375
|
+
sa.scripts[:status] << status_cmd
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Simple daemon wrapper for ar_sendmail setup and control.
|
5
|
+
# By default, uses server apps with the :mail role.
|
6
|
+
|
7
|
+
class ARSendmail < Daemon
|
8
|
+
|
9
|
+
def initialize app, options={}
|
10
|
+
options[:role] ||= :mail
|
11
|
+
|
12
|
+
super app, options
|
13
|
+
|
14
|
+
@dep_name = options[:dep_name] || 'ar_mailer'
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def start_cmd
|
19
|
+
"cd #{@app.current_path} && #{@bin} -p #{@pid} -d"
|
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
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Simple daemon wrapper for delayed_job daemon setup and control.
|
5
|
+
# By default, uses server apps with the :dj role. Supports
|
6
|
+
# the :processes option.
|
7
|
+
|
8
|
+
class DelayedJob < Daemon
|
9
|
+
|
10
|
+
def initialize app, options={}
|
11
|
+
options[:role] ||= :dj
|
12
|
+
|
13
|
+
super app, options
|
14
|
+
|
15
|
+
@pid = "#{@app.current_path}/tmp/pids/delayed_job.pid"
|
16
|
+
|
17
|
+
@dep_name = options[:dep_name] || "daemons"
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def start_cmd
|
22
|
+
"cd #{@app.current_path} && script/delayed_job -n #{@processes} start"
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def stop_cmd
|
27
|
+
"cd #{@app.current_path} && script/delayed_job stop"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|